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 if type_.text
is None:
121 type_ = type_.find(
"ref")
122 assert defname.text == declname.text
123 return {
"type": type_.text,
"name": defname.text}
127 return filename.upper().replace(
".",
"_").replace(
"/",
"_").replace(
"-",
"_")
131 b = [el.text.strip()
for el
in brief.iter()
if el.text]
if brief
is not None else []
133 [el.text.strip()
for el
in detailed.iter()
if el.text]
134 if detailed
is not None
139 text +=
"\n" +
"".join(d)
149 def xmlToType(self, node, array=None, parentClass=None, tplargs=None):
152 - parentClass: a class
153 - tplargs: if one of the args is parentClass and no template arguments are provided,
154 set the template arguments to this value
155 - array: content of the sibling tag 'array'
157 if node.text
is not None:
158 t = node.text.strip()
161 for c
in node.iterchildren():
163 refid = c.attrib[
"refid"]
164 if parentClass
is not None and refid == parentClass.id:
165 t +=
" " + parentClass.name
166 if c.tail
is not None and c.tail.lstrip()[0] !=
"<":
167 if tplargs
is not None:
170 parentClass
is not None
171 and isinstance(parentClass, ClassCompound)
172 and parentClass.hasTypeDef(c.text.strip())
174 parent_has_templates = len(parentClass.template_params) > 0
175 if parent_has_templates:
176 t +=
" typename " + parentClass._className() +
"::"
178 t +=
" " + parentClass._className() +
"::"
179 self_has_templates = (
180 c.tail
is not None and c.tail.strip().find(
"<") != -1
182 if self_has_templates:
185 elif self.
index.hasref(refid):
186 t +=
" " + self.
index.getref(refid).name
188 self.
index.output.warn(
"Unknown reference: ", c.text, refid)
189 t +=
" " + c.text.strip()
191 if c.text
is not None:
192 t +=
" " + c.text.strip()
193 if c.tail
is not None:
194 t +=
" " + c.tail.strip()
195 if array
is not None:
205 id=memberdefxml.attrib[
"id"],
206 name=memberdefxml.find(
"definition").text,
211 self.
const = memberdefxml.attrib[
"const"] ==
"yes"
212 self.
static = memberdefxml.attrib[
"static"] ==
"yes"
216 (param.find(
"type"), param.find(
"declname"), param.find(
"array"))
217 for param
in self.
xml.findall(
"param")
221 self.
rettype.text
is None and len(self.
rettype.getchildren()) == 0
242 for param
in self.
xml.findall(
"param")
250 return "({0}){1}".format(self.
s_args(),
" const" if self.
const else "")
256 if len(self.
parent.template_params) > 0:
259 +
", ".join([d[
"name"]
for d
in self.
parent.template_params])
265 type, array, parentClass=self.
parent, tplargs=tplargs
267 for type, declname, array
in self.
params
274 for type, declname, array
in self.
params
282 return ", ".join([d[
"type"] +
" " + d[
"name"]
for d
in self.
template_params])
287 ),
"Member {} ({}) is a special function and no return type".format(
290 if len(self.
parent.template_params) > 0:
293 +
", ".join([d[
"name"]
for d
in self.
parent.template_params])
298 if isinstance(self.
parent, ClassCompound):
306 return self.
xml.find(
"name").text.strip()
309 return self.
index.xml_docstring.getDocString(
310 self.
xml.find(
"briefdescription"),
311 self.
xml.find(
"detaileddescription"),
319 def getdeclname(i, declname):
320 if declname
is None or declname.text
is None or declname.text.strip() ==
"":
321 return "arg{}".format(i)
322 return declname.text.strip()
324 arg =
"""boost::python::arg("{}")"""
327 ] + [getdeclname(i, declname)
for i, (_, declname, _)
in enumerate(self.
params)]
328 return ", ".join([arg.format(n)
for n
in argnames])
331 loc = self.
xml.find(
"location")
333 return loc.attrib[
"file"].split(
"/", 1)[1]
339 self.
filename = path.join(index.directory, compound.attrib[
"refid"] +
".xml")
345 name=self.
definition.find(
"compoundname").text,
351 super(NamespaceCompound, self).
__init__(*args)
358 for section
in self.
definition.iterchildren(
"sectiondef"):
359 assert "kind" in section.attrib
360 kind = section.attrib[
"kind"]
363 elif kind ==
"typedef":
369 for member
in section.iterchildren(
"memberdef"):
372 id=member.attrib[
"id"],
373 name=self.
name +
"::" + member.find(
"name").text,
375 self.
index.registerReference(ref)
376 self.
enums.append(member)
377 for value
in member.iterchildren(
"enumvalue"):
380 id=value.attrib[
"id"],
381 name=self.
name +
"::" + member.find(
"name").text,
385 for member
in section.iterchildren(
"memberdef"):
388 id=member.attrib[
"id"],
389 name=self.
name +
"::" + member.find(
"name").text,
391 self.
index.registerReference(ref)
395 for member
in section.iterchildren(
"memberdef"):
405 class ClassCompound(CompoundBase):
407 super(ClassCompound, self).
__init__(*args)
420 for memberdef
in self.
definition.iter(tag=
"memberdef"):
421 if memberdef.attrib[
"prot"] !=
"public":
423 if memberdef.attrib[
"kind"] ==
"variable":
425 elif memberdef.attrib[
"kind"] ==
"typedef":
428 id=memberdef.attrib[
"id"],
429 name=self.
_className() +
"::" + memberdef.find(
"name").text,
431 self.
index.registerReference(ref)
432 self.
typedef[memberdef.find(
"name").text.strip()] =
True
434 elif memberdef.attrib[
"kind"] ==
"enum":
435 if memberdef.find(
"name").text
is None:
436 ref_name = self.
_className() +
"::" +
"anonymous_enum"
438 ref_name = self.
_className() +
"::" + memberdef.find(
"name").text
441 id=memberdef.attrib[
"id"],
444 self.
index.registerReference(ref)
445 for value
in memberdef.iterchildren(
"enumvalue"):
448 id=value.attrib[
"id"],
451 self.
index.registerReference(value_ref)
452 elif memberdef.attrib[
"kind"] ==
"function":
464 if not hasattr(self,
"template_params")
or len(self.
template_params) == 0:
466 return ", ".join([d[
"type"] +
" " + d[
"name"]
for d
in self.
template_params])
469 if not hasattr(self,
"template_params")
or len(self.
template_params) == 0:
479 return typename
in self.
typedef
494 docstring = self.
index.xml_docstring.getDocString(
499 attribute_docstrings =
""
501 _dc = self.
index.xml_docstring.getDocString(
502 member.find(
"briefdescription"),
503 member.find(
"detaileddescription"),
508 attribute_docstrings += template_class_attribute_body.format(
509 attribute=member.find(
"name").text,
512 if len(docstring) == 0
and len(attribute_docstrings) == 0:
515 template_class_doc.format(
519 attributes=attribute_docstrings,
528 "Disable class {} because template argument are not resolved for templated class specialization.".format(
536 output.err(
"Does not know where to write doc of", self.
name)
538 output.open(include.text)
539 output.out(template_include_extern.format(filename=include.text))
540 output.out(template_open_namespace.format(namespace=
"doxygen"))
546 member_funcs = dict()
548 prototype = m.prototypekey()
549 if prototype
in member_funcs:
550 member_funcs[prototype].append(m)
552 member_funcs[prototype] = [
559 docstring = member.s_docstring()
560 argnamesstring = member.s_argnamesstring()
561 if len(docstring) == 0
and len(argnamesstring) == 0:
563 if member.s_name()[0] ==
"~":
565 template_destructor_doc.format(
573 template_constructor_doc.format(
576 d[
"type"] +
" " + d[
"name"]
580 nargs=len(member.params),
581 comma=
", " if len(member.params) > 0
else "",
583 argsstring=member.s_args(),
585 argnamesstring=argnamesstring,
589 for prototype, members
in member_funcs.items():
591 documented_members = []
594 for member
in members:
595 docstring = member.s_docstring()
596 argnamesstring = member.s_argnamesstring()
597 if len(docstring) == 0
and len(argnamesstring) == 0:
599 documented_members.append(member)
600 docstrings.append(docstring)
601 argnamesstrings.append(argnamesstring)
602 if len(documented_members) == 0:
608 template_member_func_doc_body.format(
609 classname_prefix=classname_prefix,
610 membername=member.s_name(),
612 rettype=member.s_rettype(),
613 argsstring=member.s_prototypeArgs(),
615 for member, docstring
in zip(documented_members, docstrings)
622 d[
"type"] +
" " + d[
"name"]
627 template_member_func_doc.format(
629 "template <{}>\n".format(tplargs)
if len(tplargs) > 0
else ""
631 rettype=member.s_rettype(),
632 classname_prefix=classname_prefix,
633 argsstring=member.s_prototypeArgs(),
641 template_member_func_args_body.format(
642 classname_prefix=classname_prefix,
643 membername=member.s_name(),
645 rettype=member.s_rettype(),
646 argsstring=member.s_prototypeArgs(),
648 for member, argnamesstring
in zip(
649 documented_members, argnamesstrings
654 n_args = member.n_args()
656 default_args =
", ".join(
658 """boost::python::arg("self")""",
660 + [
"""boost::python::arg("arg{}")""".format(i)
for i
in range(n_args)]
663 template_member_func_args.format(
665 "template <{}>\n".format(tplargs)
if len(tplargs) > 0
else ""
667 rettype=member.s_rettype(),
669 default_args=default_args,
670 classname_prefix=classname_prefix,
671 argsstring=member.s_prototypeArgs(),
676 output.out(template_close_namespace.format(namespace=
"doxygen"))
685 This class is responsible for generating the list of all C++-usable documented elements.
697 for compound
in self.
tree.getroot().iterchildren(
"compound"):
698 if compound.attrib[
"kind"]
in [
"class",
"struct"]:
700 elif compound.attrib[
"kind"] ==
"namespace":
709 self.
output.open(
"doxygen_xml_parser_for_cmake.hh")
716 compound.write(self.
output)
718 self.
output.open(
"functions.h")
721 static_funcs = dict()
726 for m
in compound.static_funcs:
727 include = m.include()
728 if include
not in includes:
729 includes.append(include)
730 docstring = m.s_docstring()
731 if len(docstring) == 0:
733 prototype = m.prototypekey()
734 if prototype
in static_funcs:
735 static_funcs[prototype].append((m, docstring))
737 static_funcs[prototype] = [
740 prototypes.append(prototype)
745 template_include_extern.format(filename=filename)
746 for filename
in includes
751 self.
output.out(template_open_namespace.format(namespace=
"doxygen"))
753 for prototype
in prototypes:
754 member_and_docstring_s = static_funcs[prototype]
757 template_static_func_doc_body.format(
758 namespace=member.parent.innerNamespace(),
759 membername=member.s_name(),
761 rettype=member.s_rettype(),
762 argsstring=member.s_prototypeArgs(),
764 for member, docstring
in member_and_docstring_s
768 member = member_and_docstring_s[0][0]
772 d[
"type"] +
" " + d[
"name"]
773 for d
in member.parent.template_params + member.template_params
777 template_static_func_doc.format(
779 "template <{}>\n".format(tplargs)
if len(tplargs) > 0
else ""
781 rettype=member.s_rettype(),
782 argsstring=member.s_prototypeArgs(),
787 self.
output.out(template_close_namespace.format(namespace=
"doxygen"))
794 "!!!! Compounds " + obj.id +
" already exists.",
799 self.
output.warn(
"Reference " + obj.id +
" already exists.", obj.name)
812 def __init__(self, output_dir, warn, error, errorPrefix=""):
822 assert self.
_out is None,
"You did not close the previous file"
825 fullname = os.path.join(self.
output_dir, name)
826 dirname = os.path.dirname(fullname)
827 if not os.path.isdir(dirname):
835 if sys.version_info >= (3,):
839 self.
_out = codecs.open(fullname, mode=
"w", encoding=encoding)
844 template_file_header.format(
845 path=os.path.dirname(os.path.abspath(__file__)),
858 template_file_footer.format(
867 if sys.version_info >= (3,):
868 print(*args, file=self.
_out)
870 print(
" ".join(
str(arg)
for arg
in args).decode(
"latin1"), file=self.
_out)
879 if __name__ ==
"__main__":
882 parser = argparse.ArgumentParser(
883 description=
"Process Doxygen XML documentation and generate C++ code."
885 parser.add_argument(
"doxygen_index_xml", type=str, help=
"the Doxygen XML index.")
886 parser.add_argument(
"output_directory", type=str, help=
"the output directory.")
887 args = parser.parse_args()
891 output=
OutputStreams(args.output_directory, sys.stdout, sys.stderr),
893 index.parseCompound()
895 index.output.writeFooterAndCloseFiles()
896 assert index.output._out
is None