1# -*- coding: utf-8 -*-
2import os
3import sys
4import errno
5import xml.etree.ElementTree
6
7
8ALLOWED_TYPES = ['class', 'interface', 'struct', 'union']
9
10
11def write_file(name, text, destdir):
12    """Write the output file for module/package <name>."""
13    fname = os.path.join(destdir, '%s.%s' % (name, 'rst'))
14
15    if not os.path.exists(os.path.dirname(fname)):
16        try:
17            os.makedirs(os.path.dirname(fname))
18        except OSError as exc:  # Guard against race condition
19            if exc.errno != errno.EEXIST:
20                raise
21    try:
22        with open(fname, 'r') as target:
23            orig = target.read()
24            if orig == text:
25                return
26    except FileNotFoundError:
27        # Don't mind if it isn't there
28        pass
29
30    with open(fname, 'w') as target:
31        target.write(text)
32
33
34def format_heading(level, text):
35    """Create a heading of <level> [1, 2 or 3 supported]."""
36    underlining = ['=', '-', '~', ][level - 1] * len(text)
37    return '%s\n%s\n\n' % (text, underlining)
38
39
40def format_directive(package_type, package, project = None):
41    """Create the breathe directive and add the options."""
42    directive = '.. doxygen%s:: %s\n' % (package_type, package)
43    if project:
44        directive += '   :project: %s\n' % project
45    return directive
46
47
48def create_package_file(package, package_type, package_id, package_folder, rootpath, destdir):
49    """Build the text of the file and write the file."""
50    text = format_heading(1, '%s' % (package))
51    text += format_directive(package_type, package)
52
53    xmlfile = os.path.join(rootpath, package_id + '.xml')
54    f = xml.etree.ElementTree.parse(os.path.join(xmlfile))
55
56    write_file(os.path.join(package_folder, package_id), text, destdir)
57
58
59def create_modules_toc_file(key, value, destdir):
60    """Create the module's index."""
61    text = format_heading(1, '%s' % value)
62    text += '.. toctree::\n'
63    text += '   :glob:\n\n'
64    text += '   %s/*\n' % key
65
66    write_file('%slist' % key, text, destdir)
67
68
69def get_compound_folder(rootpath, compound):
70    fxml = xml.etree.ElementTree.parse(os.path.join(rootpath, compound.get('refid')) + '.xml')
71    loc = fxml.getroot()[0].find('location')
72    dirname = os.path.basename(os.path.split(loc.get('file'))[0])
73    return dirname
74
75
76def recurse_tree(rootpath, destdir):
77    """
78    Look for every file in the directory tree and create the corresponding
79    ReST files.
80    """
81    index = xml.etree.ElementTree.parse(os.path.join(rootpath, 'index.xml'))
82
83    for compound in index.getroot():
84        if compound.get('kind') not in ALLOWED_TYPES:
85            continue
86        create_package_file(compound.findtext('name'), compound.get('kind'),
87                            compound.get('refid'), get_compound_folder(rootpath, compound),
88                            rootpath, destdir)
89
90def get_folders_tree(rootpath):
91    tmp = []
92
93    # Retrieve the subfolders indexes
94    for root, _, files in os.walk(rootpath):
95        for xmlfile in files:
96            if not xmlfile.startswith('dir_'):
97                continue
98            tmp.append(xmlfile)
99
100    # Iterate on them
101    dirs = []
102    for xmlfile in tmp:
103        data = xml.etree.ElementTree.parse(os.path.join(rootpath, xmlfile))
104        if not data:
105            continue
106        for compound in data.getroot():
107            name = compound.findtext('compoundname')
108            dirs.append(name)
109
110    return dirs
111
112
113def main():
114    rootpath = './doxygen-out/xml'
115    destdir = './source/api'
116
117    if not os.path.exists(destdir):
118        os.makedirs(destdir)
119
120    dirs = sorted(get_folders_tree(rootpath))
121    source_root = dirs[0]
122    source_dirs = dirs[1:]
123    out_dirs = [os.path.basename(d) for d in dirs]
124
125    # TODO Handle only one level subfolders
126    for key in out_dirs:
127        ddir = os.path.join(destdir, key)
128        if not os.path.exists(ddir):
129            os.makedirs(ddir)
130        create_modules_toc_file(key, key.capitalize(), destdir)
131    recurse_tree(rootpath, destdir)
132
133
134# So program can be started with "python -m breathe.apidoc ..."
135if __name__ == "__main__":
136    main()
137