aboutsummaryrefslogtreecommitdiff
path: root/docs/doc_builder.py
diff options
context:
space:
mode:
authorKevin Schlosser <kdschlosser@users.noreply.github.com>2023-04-27 06:42:02 -0600
committerGitHub <noreply@github.com>2023-04-27 14:42:02 +0200
commite485dd8bb400ef29469311591656936ae9beffb8 (patch)
treed3cfbcdcaedafc3c202c184d1029824833ad1f3d /docs/doc_builder.py
parente7f88efa5853128bf871dde335c0ca8da9eb7731 (diff)
downloadlvgl-e485dd8bb400ef29469311591656936ae9beffb8.tar.gz
lvgl-e485dd8bb400ef29469311591656936ae9beffb8.zip
feat(docs): migrate from .md to .rst (#4129)
Diffstat (limited to 'docs/doc_builder.py')
-rw-r--r--docs/doc_builder.py713
1 files changed, 713 insertions, 0 deletions
diff --git a/docs/doc_builder.py b/docs/doc_builder.py
new file mode 100644
index 000000000..ec9cd46b7
--- /dev/null
+++ b/docs/doc_builder.py
@@ -0,0 +1,713 @@
+import os
+from xml.etree import ElementTree as ET
+
+base_path = ''
+xml_path = ''
+
+
+def load_xml(fle):
+ fle = os.path.join(xml_path, fle + '.xml')
+
+ with open(fle, 'rb') as f:
+ d = f.read().decode('utf-8')
+
+ # This code is to correct a bug in Doxygen. That bug incorrectly parses
+ # a typedef and it causes an error to occur building the docs. The Error
+ # doesn't stop the documentation from being generated, I just don't want
+ # to see the ugly red output.
+ #
+ # if 'typedef void() lv_lru_free_t(void *v)' in d:
+ # d = d.replace(
+ # '<type>void()</type>\n '
+ # '<definition>typedef void() lv_lru_free_t(void *v)</definition>',
+ # '<type>void</type>\n '
+ # '<definition>typedef void(lv_lru_free_t)(void *v)</definition>'
+ # )
+ # with open(fle, 'wb') as f:
+ # f.write(d.encode('utf-8'))
+
+ return ET.fromstring(d)
+
+
+structures = {}
+functions = {}
+enums = {}
+typedefs = {}
+variables = {}
+unions = {}
+namespaces = {}
+files = {}
+
+
+class STRUCT(object):
+ template = '''\
+.. doxygenstruct:: {name}
+ :project: lvgl
+ :members:
+ :protected-members:
+ :private-members:
+ :undoc-members:
+'''
+
+ def __init__(self, parent, refid, name, **_):
+ if name in structures:
+ self.__dict__.update(structures[name].__dict__)
+ return
+
+ structures[name] = self
+ self.parent = parent
+ self.refid = refid
+ self.name = name
+ self.types = set()
+ self._deps = None
+ self.header_file = ''
+
+ root = load_xml(refid)
+
+ for compounddef in root:
+ if compounddef.attrib['id'] != self.refid:
+ continue
+
+ for child in compounddef:
+ if child.tag == 'includes':
+ self.header_file = os.path.splitext(child.text)[0]
+
+ if child.tag != 'sectiondef':
+ continue
+
+ for memberdef in child:
+ t = get_type(memberdef)
+
+ if t is None:
+ continue
+
+ self.types.add(t)
+
+ @property
+ def deps(self):
+ if self._deps is None:
+ self._deps = dict(
+ typedefs=set(),
+ functions=set(),
+ enums=set(),
+ structures=set(),
+ unions=set(),
+ namespaces=set(),
+ variables=set(),
+ )
+ for type_ in self.types:
+ if type_ in typedefs:
+ self._deps['typedefs'].add(typedefs[type_])
+ elif type_ in structures:
+ self._deps['structures'].add(structures[type_])
+ elif type_ in unions:
+ self._deps['unions'].add(unions[type_])
+ elif type_ in enums:
+ self._deps['enums'].add(enums[type_])
+ elif type_ in functions:
+ self._deps['functions'].add(functions[type_])
+ elif type_ in variables:
+ self._deps['variables'].add(variables[type_])
+ elif type_ in namespaces:
+ self._deps['namespaces'].add(namespaces[type_])
+ return self._deps
+
+ def __str__(self):
+ return self.template.format(name=self.name)
+
+
+class UNION(STRUCT):
+ template = '''\
+.. doxygenunion:: {name}
+ :project: lvgl
+'''
+
+
+def get_type(node):
+ def gt(n):
+ for c in n:
+ if c.tag == 'ref':
+ t = c.text.strip()
+ break
+ else:
+ t = node.text.strip()
+
+ return t.replace('*', '').replace('(', '').replace(')', '').strip()
+
+ for child in node:
+ if child.tag == 'type':
+ return gt(child)
+
+
+class VARIABLE(object):
+ template = '''\
+.. doxygenvariable:: {name}
+ :project: lvgl
+'''
+
+ def __init__(self, parent, refid, name, **_):
+ if name in variables:
+ self.__dict__.update(variables[name].__dict__)
+ return
+
+ variables[name] = self
+ self.parent = parent
+ self.refid = refid
+ self.name = name
+
+ def __str__(self):
+ return self.template.format(name=self.name)
+
+
+class NAMESPACE(object):
+ template = '''\
+.. doxygennamespace:: {name}
+ :project: lvgl
+ :members:
+ :protected-members:
+ :private-members:
+ :undoc-members:
+'''
+
+ def __init__(self, parent, refid, name, **_):
+ if name in namespaces:
+ self.__dict__.update(namespaces[name].__dict__)
+ return
+
+ namespaces[name] = self
+ self.parent = parent
+ self.refid = refid
+ self.name = name
+
+ def __str__(self):
+ return self.template.format(name=self.name)
+
+
+class FUNCTION(object):
+ template = '''\
+.. doxygenfunction:: {name}
+ :project: lvgl
+'''
+
+ def __init__(self, parent, refid, name, **_):
+ if name in functions:
+ self.__dict__.update(functions[name].__dict__)
+ return
+
+ functions[name] = self
+ self.parent = parent
+ self.refid = refid
+ self.name = name
+ self.types = set()
+ self.restype = None
+ self._deps = None
+
+ if parent is not None:
+ root = load_xml(parent.refid)
+
+ for compounddef in root:
+ if compounddef.attrib['id'] != parent.refid:
+ continue
+
+ for child in compounddef:
+ if child.tag != 'sectiondef':
+ continue
+ if child.attrib['kind'] != 'func':
+ continue
+
+ for memberdef in child:
+ if memberdef.attrib['id'] == refid:
+ break
+ else:
+ continue
+
+ break
+ else:
+ continue
+
+ break
+ else:
+ return
+
+ self.restype = get_type(memberdef)
+
+ for child in memberdef:
+ if child.tag == 'param':
+ t = get_type(child)
+ if t is not None:
+ self.types.add(t)
+
+ if self.restype in self.types:
+ self.restype = None
+
+ @property
+ def deps(self):
+ if self._deps is None:
+ self._deps = dict(
+ typedefs=set(),
+ functions=set(),
+ enums=set(),
+ structures=set(),
+ unions=set(),
+ namespaces=set(),
+ variables=set(),
+ )
+ if self.restype is not None:
+ self.types.add(self.restype)
+
+ for type_ in self.types:
+ if type_ in typedefs:
+ self._deps['typedefs'].add(typedefs[type_])
+ elif type_ in structures:
+ self._deps['structures'].add(structures[type_])
+ elif type_ in unions:
+ self._deps['unions'].add(unions[type_])
+ elif type_ in enums:
+ self._deps['enums'].add(enums[type_])
+ elif type_ in functions:
+ self._deps['functions'].add(functions[type_])
+ elif type_ in variables:
+ self._deps['variables'].add(variables[type_])
+ elif type_ in namespaces:
+ self._deps['namespaces'].add(namespaces[type_])
+ return self._deps
+
+ def __str__(self):
+ return self.template.format(name=self.name)
+
+
+class FILE(object):
+ def __init__(self, _, refid, name, node, **__):
+ if name in files:
+ self.__dict__.update(files[name].__dict__)
+ return
+
+ files[name] = self
+
+ self.refid = refid
+ self.name = name
+ self.header_file = os.path.splitext(name)[0]
+
+ enums_ = []
+
+ for member in node:
+ if member.tag != 'member':
+ continue
+
+ cls = globals()[member.attrib['kind'].upper()]
+ if cls == ENUM:
+ member.attrib['name'] = member[0].text.strip()
+ enums_.append(cls(self, **member.attrib))
+ elif cls == ENUMVALUE:
+ if enums_[-1].is_member(member):
+ enums_[-1].add_member(member)
+
+ else:
+ member.attrib['name'] = member[0].text.strip()
+ cls(self, **member.attrib)
+
+
+class ENUM(object):
+ template = '''\
+.. doxygenenum:: {name}
+ :project: lvgl
+'''
+
+ def __init__(self, parent, refid, name, **_):
+ if name in enums:
+ self.__dict__.update(enums[name].__dict__)
+ return
+
+ enums[name] = self
+
+ self.parent = parent
+ self.refid = refid
+ self.name = name
+ self.members = []
+
+ def is_member(self, member):
+ return (
+ member.attrib['kind'] == 'enumvalue' and
+ member.attrib['refid'].startswith(self.refid)
+ )
+
+ def add_member(self, member):
+ self.members.append(
+ ENUMVALUE(
+ self,
+ member.attrib['refid'],
+ member[0].text.strip()
+ )
+ )
+
+ def __str__(self):
+ template = [self.template.format(name=self.name)]
+ template.extend(list(str(member) for member in self.members))
+
+ return '\n'.join(template)
+
+
+defines = {}
+
+
+class DEFINE(object):
+ template = '''\
+.. doxygendefine:: {name}
+ :project: lvgl
+'''
+
+ def __init__(self, parent, refid, name, **_):
+ if name in defines:
+ self.__dict__.update(defines[name].__dict__)
+ return
+
+ defines[name] = self
+
+ self.parent = parent
+ self.refid = refid
+ self.name = name
+
+ def __str__(self):
+ return self.template.format(name=self.name)
+
+
+class ENUMVALUE(object):
+ template = '''\
+.. doxygenenumvalue:: {name}
+ :project: lvgl
+'''
+
+ def __init__(self, parent, refid, name, **_):
+ self.parent = parent
+ self.refid = refid
+ self.name = name
+
+ def __str__(self):
+ return self.template.format(name=self.name)
+
+
+class TYPEDEF(object):
+ template = '''\
+.. doxygentypedef:: {name}
+ :project: lvgl
+'''
+
+ def __init__(self, parent, refid, name, **_):
+ if name in typedefs:
+ self.__dict__.update(typedefs[name].__dict__)
+ return
+
+ typedefs[name] = self
+
+ self.parent = parent
+ self.refid = refid
+ self.name = name
+ self.type = None
+ self._deps = None
+
+ if parent is not None:
+ root = load_xml(parent.refid)
+
+ for compounddef in root:
+ if compounddef.attrib['id'] != parent.refid:
+ continue
+
+ for child in compounddef:
+ if child.tag != 'sectiondef':
+ continue
+ if child.attrib['kind'] != 'typedef':
+ continue
+
+ for memberdef in child:
+ if memberdef.attrib['id'] == refid:
+ break
+ else:
+ continue
+
+ break
+ else:
+ continue
+
+ break
+ else:
+ return
+
+ self.type = get_type(memberdef)
+
+ @property
+ def deps(self):
+ if self._deps is None:
+ self._deps = dict(
+ typedefs=set(),
+ functions=set(),
+ enums=set(),
+ structures=set(),
+ unions=set(),
+ namespaces=set(),
+ variables=set(),
+ )
+ if self.type is not None:
+ type_ = self.type
+
+ if type_ in typedefs:
+ self._deps['typedefs'].add(typedefs[type_])
+ elif type_ in structures:
+ self._deps['structures'].add(structures[type_])
+ elif type_ in unions:
+ self._deps['unions'].add(unions[type_])
+ elif type_ in enums:
+ self._deps['enums'].add(enums[type_])
+ elif type_ in functions:
+ self._deps['functions'].add(functions[type_])
+ elif type_ in variables:
+ self._deps['variables'].add(variables[type_])
+ elif type_ in namespaces:
+ self._deps['namespaces'].add(namespaces[type_])
+
+ return self._deps
+
+ def __str__(self):
+ return self.template.format(name=self.name)
+
+
+classes = {}
+
+
+class CLASS(object):
+
+ def __init__(self, _, refid, name, node, **__):
+ if name in classes:
+ self.__dict__.update(classes[name].__dict__)
+ return
+
+ classes[name] = self
+
+ self.refid = refid
+ self.name = name
+
+ enums_ = []
+
+ for member in node:
+ if member.tag != 'member':
+ continue
+
+ cls = globals()[member.attrib['kind'].upper()]
+ if cls == ENUM:
+ member.attrib['name'] = member[0].text.strip()
+ enums_.append(cls(self, **member.attrib))
+ elif cls == ENUMVALUE:
+ if enums_[-1].is_member(member):
+ enums_[-1].add_member(member)
+
+ else:
+ member.attrib['name'] = member[0].text.strip()
+ cls(self, **member.attrib)
+
+
+lvgl_src_path = ''
+api_path = ''
+html_files = {}
+
+
+def iter_src(n, p):
+ if p:
+ out_path = os.path.join(api_path, p)
+ else:
+ out_path = api_path
+
+ index_file = None
+
+ if p:
+ src_path = os.path.join(lvgl_src_path, p)
+ else:
+ src_path = lvgl_src_path
+
+ folders = []
+
+ for file in os.listdir(src_path):
+ if 'private' in file:
+ continue
+
+ if os.path.isdir(os.path.join(src_path, file)):
+ folders.append((file, os.path.join(p, file)))
+ continue
+
+ if not file.endswith('.h'):
+ continue
+
+ if not os.path.exists(out_path):
+ os.makedirs(out_path)
+
+ if index_file is None:
+ index_file = open(os.path.join(out_path, 'index.rst'), 'w')
+ if n:
+ index_file.write('=' * len(n))
+ index_file.write('\n' + n + '\n')
+ index_file.write('=' * len(n))
+ index_file.write('\n\n\n')
+
+ index_file.write('.. toctree::\n :maxdepth: 2\n\n')
+
+ name = os.path.splitext(file)[0]
+ index_file.write(' ' + name + '\n')
+
+ rst_file = os.path.join(out_path, name + '.rst')
+ html_file = os.path.join(p, name + '.html')
+ html_files[name] = html_file
+
+ with open(rst_file, 'w') as f:
+ f.write('.. _{0}:'.format(name))
+ f.write('\n\n')
+ f.write('=' * len(file))
+ f.write('\n')
+ f.write(file)
+ f.write('\n')
+ f.write('=' * len(file))
+ f.write('\n\n\n')
+
+ f.write('.. doxygenfile:: ' + file)
+ f.write('\n')
+ f.write(' :project: lvgl')
+ f.write('\n\n')
+
+ for name, folder in folders:
+ if iter_src(name, folder):
+ if index_file is None:
+ index_file = open(os.path.join(out_path, 'index.rst'), 'w')
+
+ if n:
+ index_file.write('=' * len(n))
+ index_file.write('\n' + n + '\n')
+ index_file.write('=' * len(n))
+ index_file.write('\n\n\n')
+
+ index_file.write('.. toctree::\n :maxdepth: 2\n\n')
+
+ index_file.write(' ' + os.path.split(folder)[-1] + '/index\n')
+
+ if index_file is not None:
+ index_file.write('\n')
+ index_file.close()
+ return True
+
+ return False
+
+
+def clean_name(nme):
+ if nme.startswith('_lv_'):
+ nme = nme[4:]
+ elif nme.startswith('lv_'):
+ nme = nme[3:]
+
+ if nme.endswith('_t'):
+ nme = nme[:-2]
+
+ return nme
+
+
+def is_name_match(item_name, obj_name):
+ u_num = item_name.count('_') + 1
+
+ obj_name = obj_name.split('_')
+ if len(obj_name) < u_num:
+ return False
+ obj_name = '_'.join(obj_name[:u_num])
+
+ return item_name == obj_name
+
+
+def get_includes(name1, name2, obj, includes):
+ name2 = clean_name(name2)
+
+ if not is_name_match(name1, name2):
+ return
+
+ if obj.parent is not None:
+ header_file = obj.parent.header_file
+ elif hasattr(obj, 'header_file'):
+ header_file = obj.header_file
+ else:
+ return
+
+ if not header_file:
+ return
+
+ if header_file not in html_files:
+ return
+
+ includes.add((header_file, html_files[header_file]))
+
+
+def run(project_path, temp_directory, *doc_paths):
+ global base_path
+ global xml_path
+ global lvgl_src_path
+ global api_path
+
+ base_path = temp_directory
+ xml_path = os.path.join(base_path, 'xml')
+ api_path = os.path.join(base_path, 'API')
+ lvgl_src_path = os.path.join(project_path, 'src')
+
+ if not os.path.exists(api_path):
+ os.makedirs(api_path)
+
+ iter_src('API', '')
+ index = load_xml('index')
+
+ for compound in index:
+ compound.attrib['name'] = compound[0].text.strip()
+ if compound.attrib['kind'] in ('example', 'page', 'dir'):
+ continue
+
+ globals()[compound.attrib['kind'].upper()](
+ None,
+ node=compound,
+ **compound.attrib
+ )
+
+ for folder in doc_paths:
+ items = list(
+ (os.path.splitext(item)[0], os.path.join(folder, item))
+ for item in os.listdir(folder)
+ if item.endswith('rst') and 'index' not in item
+ )
+
+ for name, path in items:
+ html_includes = set()
+
+ for container in (
+ defines,
+ enums,
+ variables,
+ namespaces,
+ structures,
+ unions,
+ typedefs,
+ functions
+ ):
+ for n, o in container.items():
+ get_includes(name, n, o, html_includes)
+
+ if html_includes:
+ html_includes = list(
+ ':ref:`{0}`\n'.format(inc)
+ for inc, _ in html_includes
+ )
+
+ output = ('\n'.join(html_includes)) + '\n'
+
+ with open(path, 'rb') as f:
+ try:
+ data = f.read().decode('utf-8')
+ except UnicodeDecodeError:
+ print(path)
+ raise
+
+ data = data.split('.. Autogenerated', 1)[0]
+
+ data += '.. Autogenerated\n\n'
+ data += output
+
+ with open(path, 'wb') as f:
+ f.write(data.encode('utf-8'))