summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaudius "keldu" Holeksa <mail@keldu.de>2024-04-11 18:25:22 +0200
committerClaudius "keldu" Holeksa <mail@keldu.de>2024-04-11 18:25:22 +0200
commitcb77d1fe956d6011a5739bce5eddf6f6daf80661 (patch)
tree9ecde24218eb6206d1a0a3120e42085346482515
parent204aeb03daf43e73546077c8f72538ad3b6ac75b (diff)
docs: Introduce old base scripts for handling doc generation
-rw-r--r--docs/scripts/gasp.py516
-rw-r--r--docs/scripts/make_rst.py73
2 files changed, 589 insertions, 0 deletions
diff --git a/docs/scripts/gasp.py b/docs/scripts/gasp.py
new file mode 100644
index 0000000..5160bdc
--- /dev/null
+++ b/docs/scripts/gasp.py
@@ -0,0 +1,516 @@
+#!/usr/bin/env python3
+
+import argparse
+import os
+import platform
+import re
+import sys
+import xml.etree.ElementTree as ET
+import json
+from pathlib import Path
+
+
+class GaspFileDescription:
+ def __init__(self, f_id, f_name, attribs, funcs):
+ self._id = f_id;
+ self._name = f_name;
+ self._attributes = attribs;
+ self._functions = funcs;
+ pass
+
+ def gasp_to_json(self):
+ return {
+ "id" : self._id,
+ "name" : self._name,
+ "functions" : self._functions,
+ "attributes" : self._attributes
+ };
+
+class GaspNamespaceDescription:
+ def __init__(self, ns_id, ns_name, attribs, funcs):
+ self._id = ns_id;
+ self._name = ns_name;
+ self._attributes = attribs;
+ self._functions = funcs;
+ pass
+
+ def gasp_to_json(self):
+ return {
+ "id" : self._id,
+ "name" : self._name,
+ "functions" : self._functions,
+ "attributes" : self._attributes
+ };
+
+class GaspTypeRefDescription:
+ def __init__(self, type_name, type_id):
+ self._name = type_name;
+ self._id = type_id;
+ pass
+
+ def gasp_to_json(self):
+ return {
+ "name" : self._name,
+ "id" : self._id
+ };
+
+class GaspAttributeDescription:
+ def __init__(self, member_id, name):
+ self._type_name = [];
+ self._id = member_id;
+ self._name = name;
+ self._brief_description = "";
+ self._detailed_description = [];
+ self._const = False;
+ self._static = False;
+ self._initializer = "";
+ pass
+
+ def gasp_to_json(self):
+ return {
+ "type" : self._type_name,
+ "id" : self._id,
+ "name" : self._name,
+ "brief_description" : self._brief_description,
+ "detailed_description" : self._detailed_description,
+ "static" : self._static,
+ "const" : self._const,
+ "initializer" : self._initializer
+ };
+
+class GaspFunctionDescription:
+ def __init__(self, member_id, name):
+ self._type_name = [];
+ self._id = member_id;
+ self._name = name;
+ self._brief_description = "";
+ self._detailed_description = [];
+ self._params = [];
+ self._const = False;
+ self._static = False;
+ self._virtual = False;
+ pass
+
+ def gasp_to_json(self):
+ return {
+ "type" : self._type_name,
+ "id" : self._id,
+ "name" : self._name,
+ "brief_description" : self._brief_description,
+ "detailed_description" : self._detailed_description,
+ "parameters" : self._params,
+ "static" : self._static,
+ "const" : self._const,
+ "virtual" : self._virtual
+ };
+
+class GaspClassDescription:
+ def __init__(self, class_id, name,
+ attributes,
+ functions
+ ):
+ self._id = class_id;
+ self._name = name;
+ self._brief_description = "";
+ self._detailed_description = [];
+
+ self._attributes = attributes;
+ self._functions = functions;
+
+ self._specializations = [];
+ self._is_special = False;
+
+ self._templates = [];
+ pass
+
+ def append_specialization(self, name, cls_id):
+ self._specializations.append({"name" : name, "id" : cls_id});
+ pass
+
+
+ def gasp_to_json(self):
+ return {
+ "id" : self._id,
+ "name" : self._name,
+ "brief_description" : self._brief_description,
+ "detailed_description" : self._detailed_description,
+ "attributes" : self._attributes,
+ "functions" : self._functions,
+ "specializations" : self._specializations,
+ "is_special" : self._is_special,
+ "templates" : self._templates
+ };
+
+def strip_class_name_specialization(name):
+ return ''.join(name.partition('<')[0:1]).rstrip();
+ # Sleeper function
+ # We need a reductionist approach from back to front
+ # The following part only reduces one level of depth in inner class specialization cases
+ split_name = name.split('::');
+ counts = [0,0];
+ # Technically we need to check if we are in a string literal or a char
+ counts[0] = split_name[-1].count('<');
+ counts[1] = split_name[-1].count('>');
+ if counts[0] == counts[1]:
+ split_name[-1] = ''.join(split_name[-1].partition('<')[0:1]).rstrip();
+ return '::'.join(split_name);
+ return ''.join(name.partition('<')[0:1]).rstrip();
+
+def is_class_name_specialization(name):
+ return strip_class_name_specialization != name;
+
+class GaspEncoder(json.JSONEncoder):
+ def default(self, o):
+ if "gasp_to_json" in dir(o):
+ return o.gasp_to_json();
+ return json.JSONEncoder.default(self,o);
+
+def convert_doxy_xml_type_to_type_tuple(type_tag):
+ type_tuple = [];
+ for ele in type_tag.iter():
+ if ele.tag == "type":
+ if ele.text:
+ type_tuple.append(ele.text.strip());
+ elif ele.tag == "ref":
+ refid = ele.attrib["refid"];
+ refname = ele.text;
+ type_tuple.append(GaspTypeRefDescription(
+ refname,
+ refid
+ ));
+ if ele.tail:
+ type_tuple.append(ele.tail.strip());
+ return type_tuple;
+
+def convert_doxy_xml_section_to_attribs(member_section, members_attrib):
+ for memberdef in member_section:
+ type_name = convert_doxy_xml_type_to_type_tuple(memberdef.find('type'));
+ member_id = memberdef.attrib["id"];
+ name = memberdef.find('name').text;
+ mem_brief_desc = memberdef.find('briefdescription').text;
+ mem_detail_desc = [];
+ for para in memberdef.find('detaileddescription').findall('para'):
+ mem_detail_desc.append(para.text);
+ #endfor
+
+ members_attrib[member_id]._type_name = type_name;
+ members_attrib[member_id]._brief_description = mem_brief_desc;
+ members_attrib[member_id]._detailed_description = mem_detail_desc;
+ member_static = False;
+ if memberdef.attrib['static'] == "yes":
+ member_static = True;
+ #endif
+ members_attrib[member_id]._static = member_static;
+ member_init = memberdef.find('initializer');
+ if member_init is not None:
+ init_text = member_init.text.strip();
+ if init_text.startswith('='):
+ init_text = init_text[1:].lstrip();
+ #endif
+ members_attrib[member_id]._initializer = init_text;
+ #endif
+ #endfor
+
+ pass
+def convert_doxy_xml_section_to_functions(member_section, members_func):
+ for memberdef in member_section:
+ type_name = convert_doxy_xml_type_to_type_tuple(memberdef.find('type'));
+ member_id = memberdef.attrib["id"];
+
+ mem_brief_desc = memberdef.find('briefdescription').text;
+ mem_detail_desc = [];
+ for para in memberdef.find('detaileddescription').findall('para'):
+ mem_detail_desc.append(para.text);
+ #endfor
+ params = [];
+ for par in memberdef.findall('param'):
+ declname = par.find('declname');
+ declname_txt = "";
+ if declname is not None:
+ declname_txt = declname.text;
+ params.append({
+ "type" : convert_doxy_xml_type_to_type_tuple(par.find('type')),
+ "name" : declname_txt
+ });
+ #endfor
+
+ members_func[member_id]._type_name = type_name;
+ members_func[member_id]._brief_description = mem_brief_desc;
+ members_func[member_id]._detailed_description = mem_detail_desc;
+ members_func[member_id]._params = params;
+ member_static = False;
+ if memberdef.attrib['static'] == "yes":
+ member_static = True;
+ #endif
+ members_func[member_id]._static = member_static;
+ #endfor
+ pass
+
+def convert_doxy_xml_to_class(class_tree, xml_text, namespace):
+ doxy_root = ET.fromstring(xml_text);
+
+ compound_class = doxy_root.find("compounddef");
+
+ # Find the class id ( used for files/html/etc )
+ compound_id = compound_class.attrib['id'];
+
+ # Find the class descriptions
+ class_brief_desc = compound_class.find("briefdescription");
+ class_tree._brief_description = class_brief_desc.text;
+ for para in compound_class.find('detaileddescription').findall('para'):
+ class_tree._detailed_description.append(para.text);
+
+ for section in compound_class.findall('sectiondef'):
+ section_kind = section.attrib['kind'];
+ if section_kind == 'private-attrib' or section_kind == 'public-static-attrib' or section_kind == 'public-attrib' or section_kind == 'private-static-attrib':
+ convert_doxy_xml_section_to_attribs(section.findall('memberdef'), class_tree._attributes);
+ elif section.attrib['kind'] == 'private-func':
+ convert_doxy_xml_section_to_functions(section.findall('memberdef'), class_tree._functions);
+ elif section.attrib['kind'] == 'public-func':
+ convert_doxy_xml_section_to_functions(section.findall('memberdef'), class_tree._functions);
+
+ pass
+
+def convert_doxy_file_to_file(doxy_tree, xml_text, namespace):
+ doxy_file_root = ET.fromstring(xml_text);
+
+ compound_file = doxy_file_root.find("compounddef");
+
+ compound_id = compound_file.attrib['id'];
+
+ for section in compound_file.findall('sectiondef'):
+ section_kind = section.attrib['kind'];
+ if section_kind == "var":
+ convert_doxy_xml_section_to_attribs(section.findall('memberdef'), doxy_tree["attributes"]);
+ elif section_kind == "func":
+ convert_doxy_xml_section_to_functions(section.findall('memberdef'), doxy_tree["functions"]);
+ #endif
+ pass
+
+def convert_doxy_namespace_to_namespace(doxy_tree, xml_text, namespace):
+ doxy_file_root = ET.fromstring(xml_text);
+
+ compound_file = doxy_file_root.find("compounddef");
+
+ compound_id = compound_file.attrib['id'];
+
+ for section in compound_file.findall('sectiondef'):
+ section_kind = section.attrib['kind'];
+ if section_kind == "var":
+ convert_doxy_xml_section_to_attribs(section.findall('memberdef'), doxy_tree["attributes"]);
+ elif section_kind == "func":
+ convert_doxy_xml_section_to_functions(section.findall('memberdef'), doxy_tree["functions"]);
+ #endif
+ pass
+
+def convert_doxy_index_xml_to_index(xml_text, doxy_tree, namespace):
+ doxy_index_root = ET.fromstring(xml_text);
+ for compound in doxy_index_root.findall('compound'):
+ compound_kind = compound.attrib['kind'];
+ compound_id = compound.attrib['refid'];
+ compound_name = compound.find('name').text;
+ if compound_kind == 'class' or compound_kind == 'struct':
+ attribs = {};
+ funcs = {};
+
+ for member in compound.findall('member'):
+ member_kind = member.attrib['kind'];
+ member_id = member.attrib['refid'];
+ member_name = member.find('name').text;
+ if member_kind == 'function':
+ gasp_func = GaspFunctionDescription(
+ member_id,
+ member_name
+ );
+ funcs[member_id] = gasp_func;
+ elif member_kind == 'variable':
+ gasp_attrib = GaspAttributeDescription(
+ member_id,
+ member_name
+ );
+ attribs[member_id] = gasp_attrib;
+ #endif
+ #endfor
+ # Strip the namespaced class name with the provided prefix
+ class_name = compound_name;
+ if class_name.startswith(namespace):
+ class_name = class_name[len(namespace):];
+
+ gasp_class = GaspClassDescription(
+ compound_id,
+ class_name,
+ attribs,
+ funcs
+ );
+ doxy_tree['classes'][compound_id] = gasp_class;
+ #endif
+ elif compound_kind == 'namespace' and compound_name.startswith(doxy_tree['namespace_filter']):
+ attribs = [];
+ funcs = [];
+
+ for member in compound.findall('member'):
+ member_kind = member.attrib['kind'];
+ member_id = member.attrib['refid'];
+ member_name = member.find('name').text;
+
+ if member_kind == 'function':
+ gasp_func = GaspFunctionDescription(
+ member_id,
+ member_name
+ );
+ funcs.append(member_id);
+ doxy_tree['functions'][member_id] = gasp_func;
+ elif member_kind == 'variable':
+ gasp_attrib = GaspAttributeDescription(
+ member_id,
+ member_name
+ );
+ attribs.append(member_id);
+ doxy_tree['attributes'][member_id] = gasp_attrib;
+ #endif
+ #endfor
+ gasp_namespace = GaspNamespaceDescription(
+ compound_id,
+ compound_name,
+ attribs,
+ funcs
+ );
+ doxy_tree['namespaces'][compound_id] = gasp_namespace;
+ elif compound_kind == 'file' and len(doxy_tree['namespace_filter']) == 0:
+ attribs = [];
+ funcs = [];
+
+ for member in compound.findall('member'):
+ member_kind = member.attrib['kind'];
+ member_id = member.attrib['refid'];
+ member_name = member.find('name').text;
+
+ if member_kind == 'function':
+ gasp_func = GaspFunctionDescription(
+ member_id,
+ member_name
+ );
+ funcs.append(member_id);
+ doxy_tree['functions'][member_id] = gasp_func;
+ elif member_kind == 'variable':
+ gasp_attrib = GaspAttributeDescription(
+ member_id,
+ member_name
+ );
+ attribs.append(member_id);
+ doxy_tree['attributes'][member_id] = gasp_attrib;
+ #endif
+ #endfor
+ gasp_file = GaspFileDescription(
+ compound_id,
+ compound_name,
+ attribs,
+ funcs
+ );
+ doxy_tree['files'][compound_id] = gasp_file;
+ #endif
+ #endfor
+ pass
+
+def main():
+ parser = argparse.ArgumentParser(
+ prog='gasp',
+ description='Converts Doxygen XML to a JSON usable by jinja2 templates'
+ );
+
+ parser.add_argument('xml_dir');
+ parser.add_argument(
+ '-n','--namespace', required=False,
+ help='Strips the namespace from the class names',
+ default=""
+ );
+
+ args = parser.parse_args();
+
+ namespace = args.namespace + "::";
+ if len(args.namespace) == 0:
+ namespace = "";
+
+ xml_dir = Path(args.xml_dir);
+ if xml_dir.is_file():
+ print("XML dir path is not a dir");
+ exit(-1);
+ #endif
+
+ doxy_tree = {
+ "namespace_filter" : args.namespace,
+ "classes" : {},
+ "functions" : {},
+ "attributes" : {},
+ "namespaces" : {},
+ "files" : {}
+ };
+
+ index_path = xml_dir/"index.xml";
+ if index_path.is_file():
+ index_xml_file = open(index_path, "r");
+ xml_index_text = index_xml_file.read();
+ convert_doxy_index_xml_to_index(xml_index_text, doxy_tree, namespace);
+ else:
+ print("XML Index file doesn't exist");
+ exit(-1);
+ #endif
+
+ for cls_key,cls in doxy_tree['classes'].items():
+ cls_file_name = cls._id + ".xml";
+ p = xml_dir/cls_file_name;
+ if p.is_file():
+ cls_xml_file = open(p, "r");
+ cls_xml_text = cls_xml_file.read();
+ convert_doxy_xml_to_class(cls,cls_xml_text,namespace);
+ else:
+ print("Class file is missing");
+ exit(-1);
+ #endif
+ #endfor
+
+ for key,cls in doxy_tree["classes"].items():
+ stripped_cls_name = strip_class_name_specialization(cls._name);
+ if stripped_cls_name != cls._name:
+ cls._is_special = True;
+ stripped_ids = [];
+ for k,v in doxy_tree["classes"].items():
+ if v._name == stripped_cls_name:
+ stripped_ids.append(k);
+ #endif
+ #endfor
+ if len(stripped_ids) != 1:
+ print("Panic. This is supposed to be a unique name which exists exactly once. Name: " + stripped_cls_name + "\nMatching IDs: " + json.dumps(stripped_ids, indent=2));
+ exit(-1);
+ #endif
+ doxy_tree["classes"][stripped_ids[0]].append_specialization(cls._name, key);
+ #endif
+ #endfor
+
+ for key, ns in doxy_tree["namespaces"].items():
+ ns_file_name = ns._id + ".xml";
+ p = xml_dir/ns_file_name;
+ if p.is_file():
+ ns_xml_file = open(p, "r");
+ ns_xml_text = ns_xml_file.read();
+ convert_doxy_namespace_to_namespace(doxy_tree, ns_xml_text, namespace);
+ else:
+ print("Namespace file is missing");
+ exit(-1);
+ #endfor
+
+ for key,file in doxy_tree["files"].items():
+ f_file_name = file._id + ".xml";
+ p = xml_dir/f_file_name;
+ if p.is_file():
+ f_xml_file = open(p, "r");
+ f_xml_text = f_xml_file.read();
+ convert_doxy_file_to_file(doxy_tree, f_xml_text, namespace);
+ #endfor
+
+ print(json.dumps(doxy_tree,indent=2,cls=GaspEncoder));
+ pass
+
+if __name__ == "__main__":
+ main();
+#endif
diff --git a/docs/scripts/make_rst.py b/docs/scripts/make_rst.py
new file mode 100644
index 0000000..78126b4
--- /dev/null
+++ b/docs/scripts/make_rst.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python3
+import argparse
+import json
+import jinja2
+from pathlib import Path
+
+
+def parse_args():
+ parser = argparse.ArgumentParser(
+ description="Generates source files based on a jinja2 template "
+ "and a json variable map")
+ parser.add_argument(
+ '-t', '--template', required=True,
+ help='path to the jinja2 template dir')
+ parser.add_argument(
+ '-m', '--map', required=True,
+ help='path to the json variable map file')
+ parser.add_argument(
+ '-o', '--output', required=True,
+ help='path to the output dir')
+ parser.add_argument(
+ '--title', default="C++ API Reference",
+ help='The title of the index for the API'
+ );
+
+ return parser.parse_args()
+
+
+def read_template(path):
+ with open(path, "r") as f:
+ return jinja2.Template(f.read(), keep_trailing_newline=True)
+
+
+def read_var_map(path):
+ with open(path, "r") as f:
+ return json.loads(f.read());
+ #endwith
+
+
+def main():
+ args = parse_args()
+
+ template_dir = Path(args.template);
+ template = read_template(template_dir/"class.rst.tmpl");
+ var_map = read_var_map(args.map);
+
+ var_map['title'] = args.title;
+
+ out_dir = Path(args.output);
+ for k,v in var_map["classes"].items():
+ out_name = k+".rst";
+ out_file = out_dir / out_name;
+ v["specializations"] = sorted(v["specializations"], key=lambda k: k["name"]);
+ with open(out_file, "w") as f:
+ f.write(template.render(v));
+ #endwith
+ #endfor
+
+ out_globs = out_dir / "globals.rst";
+ template_globs = read_template(template_dir/"globals.rst.tmpl");
+ with open(out_globs, "w") as f:
+ f.write(template_globs.render(var_map));
+ #endwith
+
+ out_index = out_dir / "index.rst";
+ template_index = read_template(template_dir/"index.rst.tmpl");
+ var_map["classes"] = dict(sorted(var_map["classes"].items(), key=lambda k: k[1]["name"]));
+ with open(out_index, "w") as f:
+ f.write(template_index.render(var_map));
+ pass
+
+if __name__ == "__main__":
+ main();