diff options
Diffstat (limited to 'doc/tools/doxybuilder_types.py')
| -rw-r--r-- | doc/tools/doxybuilder_types.py | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/doc/tools/doxybuilder_types.py b/doc/tools/doxybuilder_types.py new file mode 100644 index 0000000000000..24e0a57db327d --- /dev/null +++ b/doc/tools/doxybuilder_types.py @@ -0,0 +1,381 @@ +''' + Copyright 2011 by the Massachusetts + Institute of Technology. All Rights Reserved. + + Export of this software from the United States of America may + require a specific license from the United States Government. + It is the responsibility of any person or organization contemplating + export to obtain such a license before exporting. + + WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + distribute this software and its documentation for any purpose and + without fee is hereby granted, provided that the above copyright + notice appear in all copies and that both that copyright notice and + this permission notice appear in supporting documentation, and that + the name of M.I.T. not be used in advertising or publicity pertaining + to distribution of the software without specific, written prior + permission. Furthermore if you modify this software you must label + your software as modified software and not distribute it in such a + fashion that it might be confused with the original M.I.T. software. + M.I.T. makes no representations about the suitability of + this software for any purpose. It is provided "as is" without express + or implied warranty. +''' + +import sys +import os +import re + +from lxml import etree + +from docmodel import * + +exclude_types = [ 'TRUE', 'FALSE', 'KRB5_ATTR_DEPRECATED', + 'KRB5_CALLCONV', 'KRB5_CALLCONV_C', 'KRB5_CALLCONV_WRONG', + 'KRB5_GENERAL__', 'KRB5_KEYUSAGE_PA_REFERRAL', + 'KRB5_OLD_CRYPTO', + 'KRB5INT_BEGIN_DECLS', 'KRB5INT_END_DECLS', + 'krb5_cc_ops', 'krb5_octet_data' ] + +class DoxyTypes(object): + def __init__(self, xmlpath): + self.xmlpath = xmlpath + + def run_compound(self, filename, include=None): + path = '%s/%s' % (self.xmlpath,filename) + tree = etree.parse(path) + root = tree.getroot() + + brief_node = root.xpath('./compounddef/briefdescription')[0] + brief_description = self._get_brief_description(brief_node) + details_node = root.xpath('./compounddef/detaileddescription')[0] + detailed_description = self._get_detailed_description(details_node) + + fields = list() + for node in root.iterfind(".//memberdef[@kind]"): + data = {} + kind = node.attrib['kind'] + if include is None or kind in include: + if kind == 'variable': + data = self._process_variable_node(node) + else: + pass + fields.append(data) + + result = {'brief_description': brief_description, + 'detailed_description': detailed_description, + 'attributes': fields} + + return result + + + + def run(self, filename, include=None): + """ + Parses xml file generated by doxygen. + + @param filename: doxygen xml file name + @param include: members sections to include, in None -- include all + """ + path = '%s/%s' % (self.xmlpath,filename) + tree = etree.parse(path) + root = tree.getroot() + result = list() + for node in root.iterfind(".//memberdef[@kind]"): + data = {} + kind = node.attrib['kind'] + if include is None or kind in include: + if kind == 'typedef': + data = self._process_typedef_node(node) + elif kind == 'variable': + data = self._process_variable_node(node) + elif kind == 'define': + data = self._process_define_node(node) + if 'name' in data and data['name'] in exclude_types: + continue + result.append(data) + return result + + + def _process_typedef_node(self, node): + t_name = node.xpath('./name/text()')[0] + + t_Id = node.attrib['id'] + t_definition = node.xpath('./definition/text()')[0] + t_type = self._process_type_node(node.xpath("./type")[0]) + brief_node = node.xpath('./briefdescription')[0] + t_brief = self._get_brief_description(brief_node) + details_node = node.xpath('./detaileddescription')[0] + t_detailed = self._get_detailed_description(details_node) + # remove macros + t_definition = re.sub('KRB5_CALLCONV_C', '', t_definition) + t_definition = re.sub('KRB5_CALLCONV', '', t_definition) + t_definition = re.sub('\*', '\\*', t_definition) + # handle fp + if t_type[1].find('(') >= 0: + t_type = (t_type[0],None) + + typedef_descr = {'category': 'composite', + 'definition': t_definition, + 'name': t_name, + 'Id': t_Id, + 'initializer': '', + 'type': t_type[1], + 'short_description': t_brief, + 'long_description': t_detailed, + 'attributes': list() + } + if t_type[0] is not None : + filename = '%s.xml' % t_type[0] + path = '%s/%s' % (self.xmlpath,filename) + if not os.path.exists(path): + # nothing can be done + return typedef_descr + + compound_info = self.run_compound(filename) + if compound_info is not None: + brief_description = compound_info.get('brief_description') + if brief_description is not None and len(brief_description): + # override brief description + typedef_descr['short_description'] = brief_description + detailed_description = compound_info.get('detailed_description') + if detailed_description is not None and len(detailed_description): + # check if this is not a duplicate + if detailed_description.find(t_detailed) < 0: + typedef_descr['long_description'] = '%s\n%s' % \ + (detailed_description, + typedef_descr['long_description']) + typedef_descr['attributes'] = compound_info['attributes'] + return typedef_descr + + def _process_variable_node(self, node): + v_name = node.xpath('./name/text()')[0] + v_Id = node.attrib['id'] + v_definition = node.xpath('./definition/text()')[0] + v_type = self._process_type_node(node.xpath("./type")[0]) + brief_node = node.xpath('./briefdescription')[0] + v_brief = self._get_brief_description(brief_node) + details_node = node.xpath('./detaileddescription')[0] + detailed_description = self._get_detailed_description(details_node) + # remove macros + v_definition = re.sub('KRB5_CALLCONV_C', '', v_definition) + v_definition = re.sub('KRB5_CALLCONV', '', v_definition) + v_definition = re.sub('\*', '\\*', v_definition) + + variable_descr = {'category': 'variable', + 'definition': v_definition, + 'name': v_name, + 'Id': v_Id, + 'initializer': '', + 'type': v_type[1], + 'short_description': v_brief, + 'long_description': detailed_description, + 'attributes': list() + } + + return variable_descr + + def _process_define_node(self, node): + d_name = node.xpath('./name/text()')[0] + d_initializer = '' + d_type = '' + d_signature = '' + + # Process param/defname node + if len(node.xpath('./param/defname')) > 0: + prm_str = '' + prm_list = list() + for p in node.xpath("./param"): + x = self._process_paragraph_content(p) + if x is not None and len(x): + prm_list.append(x) + if prm_list is not None: + prm_str = prm_str.join(prm_list) + d_signature = " %s (%s) " % (d_name , prm_str) + d_signature = re.sub(', \)', ')', d_signature).strip() + + if len(node.xpath('./initializer')) > 0: + len_ref = len(node.xpath('./initializer/ref')) + if len(node.xpath('./initializer/ref')) > 0: + d_type = self._process_type_node(node.xpath("./initializer/ref")[0]) + if len(d_type) > 0: + len_text = len(node.xpath('./initializer/text()')) + if len_text == 0 and d_type[1]: + d_initializer = d_type[1] + if len_text > 0 and len(node.xpath('./initializer/text()')[0]) > 0: + d_initializer = node.xpath('./initializer/text()')[0] + d_type[1] + if len_text > 1: + if node.xpath('./initializer/text()')[1] is not None: + d_initializer = d_initializer + node.xpath('./initializer/text()')[1] + else: + d_initializer = node.xpath('./initializer/text()')[0] + d_Id = node.attrib['id'] + brief_node = node.xpath('./briefdescription')[0] + d_brief = self._get_brief_description(brief_node) + details_node = node.xpath('./detaileddescription')[0] + detailed_description = self._get_detailed_description(details_node) + # Condense multiline macros, stripping leading whitespace. + d_initializer = re.sub(" *\\\\\n *", " ", d_initializer) + + define_descr = {'category': 'composite', + 'definition': '', + 'name': d_name, + 'name_signature': d_signature, + 'Id': d_Id, + 'initializer': d_initializer, + 'type': '', + 'short_description': d_brief, + 'long_description': detailed_description, + 'attributes': list() + } + + return define_descr + + + def _get_brief_description(self, node): + result = list() + for p in node.xpath("./para"): + x = self._process_paragraph_content(p) + if x is not None and len(x): + result.append(x) + result = '\n'.join(result) + + return result + + + def _get_detailed_description(self, node): + """ + Description node is comprised of <para>...</para> sections. + There are few types of these sections: + a) Content section + b) Return value section -- skip + c) Parameter list section -- skip + @param node: detailed description node + """ + result = list() + for p in node.xpath("./para"): + if len(p.xpath("./simplesect[@kind='return']")): + continue + elif len(p.xpath("./parameterlist[@kind='param']")): + continue + else: + x = self._process_paragraph_content(p) + result.append(x) + result = '\n'.join(result) + + return result + + def _process_paragraph_content(self, node): + + result = list() + content = node.xpath(".//text()") + for e in content: + if node is e.getparent(): + result.append(e.strip()) + elif e.getparent().tag == 'ref': + if e.is_tail: + result.append(e.strip()) + elif e.strip().find('(') > 0: + result.append(':c:func:`%s`' % e.strip()) + elif e.isupper(): + result.append(':c:data:`%s`' % e.strip()) + else: + result.append(':c:type:`%s`' % e.strip()) + elif e.getparent().tag == 'emphasis': + if e.is_tail: + result.append(e.strip()) + else: + result.append('*%s*' % e.strip()) + elif e.getparent().tag == 'computeroutput': + if e.is_tail: + result.append(e.strip()) + else: + result.append('*%s*' % e.strip()) + elif e.getparent().tag == 'defname': + result.append('%s, ' % e.strip()) + elif e.getparent().tag == 'linebreak': + result.append('\n*%s*\n' % e.strip()) + + result = ' '.join(result) + + return result + + def _process_type_node(self, node): + """ + Type node has form + <type>type_string</type> + for build in types and + <type> + <ref refid='reference',kindref='member|compound'> + 'type_name' + </ref></type> + postfix (ex. *, **m, etc.) + </type> + for user defined types. + """ + p_id = node.xpath("./ref/@refid") + if len(p_id) == 1: + p_id = p_id[0] + elif len(p_id) == 0: + p_id = None + p_type = ' '.join(node.xpath(".//text()")) + + # remove macros + p_type = re.sub('KRB5_CALLCONV_C', ' ', p_type) + p_type = re.sub('KRB5_CALLCONV', ' ', p_type) + + return (p_id,p_type) + + def save(self, obj, templates, target_dir): + template_path = templates[obj.category] + outpath = '%s/%s.rst' % (target_dir,obj.name) + obj.save(outpath, template_path) + + + +class DoxyBuilderTypes(DoxyTypes): + def __init__(self, xmlpath, rstpath): + self.templates = { 'composite': 'type_document.tmpl'} + self.target_dir = rstpath + + super(DoxyBuilderTypes,self).__init__(xmlpath) + + def run_all(self): + self.process_typedef_nodes() + self.process_define_nodes() + + def test_run(self): + filename = 'krb5_8hin.xml' + self.run(filename) + + def process_variable_nodes(self): + filename = 'struct__krb5__octet__data.xml' + result = self.run(filename, include=['variable']) + + def process_typedef_nodes(self): + # run parser for typedefs + filename = 'krb5_8hin.xml' + result = self.run(filename, include=['typedef']) + target_dir = '%s/types' % (self.target_dir) + if not os.path.exists(target_dir): + os.makedirs(target_dir, 0755) + for t in result: + obj = DocModel(**t) + self.save(obj, self.templates, target_dir) + + def process_define_nodes(self): + # run parser for define's + filename = 'krb5_8hin.xml' + result = self.run(filename, include=['define']) + target_dir = '%s/macros' % (self.target_dir) + if not os.path.exists(target_dir): + os.makedirs(target_dir, 0755) + for t in result: + obj = DocModel(**t) + tmpl = {'composite': 'define_document.tmpl'} + self.save(obj, tmpl, target_dir) + +if __name__ == '__main__': + + builder = DoxyBuilderTypes( xml_inpath, rst_outpath) + builder.run_all() |
