1# This Source Code Form is subject to the terms of the Mozilla Public
2# License, v. 2.0. If a copy of the MPL was not distributed with this
3# file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5from __future__ import absolute_import
6
7import importlib
8import os
9import sys
10
11from sphinx.util.compat import Directive
12from sphinx.util.docstrings import prepare_docstring
13
14
15def function_reference(f, attr, args, doc):
16    lines = []
17
18    lines.extend([
19        f,
20        '-' * len(f),
21        '',
22    ])
23
24    docstring = prepare_docstring(doc)
25
26    lines.extend([
27        docstring[0],
28        '',
29    ])
30
31    arg_types = []
32
33    for t in args:
34        if isinstance(t, list):
35            inner_types = [t2.__name__ for t2 in t]
36            arg_types.append(' | ' .join(inner_types))
37            continue
38
39        arg_types.append(t.__name__)
40
41    arg_s = '(%s)' % ', '.join(arg_types)
42
43    lines.extend([
44        ':Arguments: %s' % arg_s,
45        '',
46    ])
47
48    lines.extend(docstring[1:])
49    lines.append('')
50
51    return lines
52
53
54def variable_reference(v, st_type, in_type, doc):
55    lines = [
56        v,
57        '-' * len(v),
58        '',
59    ]
60
61    docstring = prepare_docstring(doc)
62
63    lines.extend([
64        docstring[0],
65        '',
66    ])
67
68    lines.extend([
69        ':Storage Type: ``%s``' % st_type.__name__,
70        ':Input Type: ``%s``' % in_type.__name__,
71        '',
72    ])
73
74    lines.extend(docstring[1:])
75    lines.append('')
76
77    return lines
78
79
80def special_reference(v, func, typ, doc):
81    lines = [
82        v,
83        '-' * len(v),
84        '',
85    ]
86
87    docstring = prepare_docstring(doc)
88
89    lines.extend([
90        docstring[0],
91        '',
92        ':Type: ``%s``' % typ.__name__,
93        '',
94    ])
95
96    lines.extend(docstring[1:])
97    lines.append('')
98
99    return lines
100
101
102def format_module(m):
103    lines = []
104
105    for subcontext, cls in sorted(m.SUBCONTEXTS.items()):
106        lines.extend([
107            '.. _mozbuild_subcontext_%s:' % subcontext,
108            '',
109            'Sub-Context: %s' % subcontext,
110            '=============' + '=' * len(subcontext),
111            '',
112        ])
113        lines.extend(prepare_docstring(cls.__doc__))
114        if lines[-1]:
115            lines.append('')
116
117        for k, v in sorted(cls.VARIABLES.items()):
118            lines.extend(variable_reference(k, *v))
119
120    lines.extend([
121        'Variables',
122        '=========',
123        '',
124    ])
125
126    for v in sorted(m.VARIABLES):
127        lines.extend(variable_reference(v, *m.VARIABLES[v]))
128
129    lines.extend([
130        'Functions',
131        '=========',
132        '',
133    ])
134
135    for func in sorted(m.FUNCTIONS):
136        lines.extend(function_reference(func, *m.FUNCTIONS[func]))
137
138    lines.extend([
139        'Special Variables',
140        '=================',
141        '',
142    ])
143
144    for v in sorted(m.SPECIAL_VARIABLES):
145        lines.extend(special_reference(v, *m.SPECIAL_VARIABLES[v]))
146
147    return lines
148
149
150class MozbuildSymbols(Directive):
151    """Directive to insert mozbuild sandbox symbol information."""
152
153    required_arguments = 1
154
155    def run(self):
156        module = importlib.import_module(self.arguments[0])
157        fname = module.__file__
158        if fname.endswith('.pyc'):
159            fname = fname[0:-1]
160
161        self.state.document.settings.record_dependencies.add(fname)
162
163        # We simply format out the documentation as rst then feed it back
164        # into the parser for conversion. We don't even emit ourselves, so
165        # there's no record of us.
166        self.state_machine.insert_input(format_module(module), fname)
167
168        return []
169
170
171def setup(app):
172    app.add_directive('mozbuildsymbols', MozbuildSymbols)
173
174    # Unlike typical Sphinx installs, our documentation is assembled from
175    # many sources and staged in a common location. This arguably isn't a best
176    # practice, but it was the easiest to implement at the time.
177    #
178    # Here, we invoke our custom code for staging/generating all our
179    # documentation.
180    from moztreedocs import SphinxManager
181
182    topsrcdir = app.config._raw_config['topsrcdir']
183    manager = SphinxManager(topsrcdir,
184        os.path.join(topsrcdir, 'tools', 'docs'),
185        app.outdir)
186    manager.generate_docs(app)
187
188    app.srcdir = os.path.join(app.outdir, '_staging')
189
190    # We need to adjust sys.path in order for Python API docs to get generated
191    # properly. We leverage the in-tree virtualenv for this.
192    from mozbuild.virtualenv import VirtualenvManager
193
194    ve = VirtualenvManager(topsrcdir,
195        os.path.join(topsrcdir, 'dummy-objdir'),
196        os.path.join(app.outdir, '_venv'),
197        sys.stderr,
198        os.path.join(topsrcdir, 'build', 'virtualenv_packages.txt'))
199    ve.ensure()
200    ve.activate()
201