Bug 1874684 - Part 28: Return DateDuration from DifferenceISODateTime. r=mgaudet
[gecko.git] / python / mozbuild / mozbuild / sphinx.py
blob4d7afb621c69899a11eca56e82a6c3304f4a40c5
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/.
5 import importlib
6 from pathlib import Path
8 from docutils import nodes
9 from docutils.parsers.rst import Directive
10 from mots.config import FileConfig
11 from mots.directory import Directory
12 from mots.export import export_to_format
13 from sphinx.util.docstrings import prepare_docstring
14 from sphinx.util.docutils import ReferenceRole
17 def function_reference(f, attr, args, doc):
18 lines = []
20 lines.extend(
23 "-" * len(f),
24 "",
28 docstring = prepare_docstring(doc)
30 lines.extend(
32 docstring[0],
33 "",
37 arg_types = []
39 for t in args:
40 if isinstance(t, list):
41 inner_types = [t2.__name__ for t2 in t]
42 arg_types.append(" | ".join(inner_types))
43 continue
45 arg_types.append(t.__name__)
47 arg_s = "(%s)" % ", ".join(arg_types)
49 lines.extend(
51 ":Arguments: %s" % arg_s,
52 "",
56 lines.extend(docstring[1:])
57 lines.append("")
59 return lines
62 def variable_reference(v, st_type, in_type, doc):
63 lines = [
65 "-" * len(v),
66 "",
69 docstring = prepare_docstring(doc)
71 lines.extend(
73 docstring[0],
74 "",
78 lines.extend(
80 ":Storage Type: ``%s``" % st_type.__name__,
81 ":Input Type: ``%s``" % in_type.__name__,
82 "",
86 lines.extend(docstring[1:])
87 lines.append("")
89 return lines
92 def special_reference(v, func, typ, doc):
93 lines = [
95 "-" * len(v),
96 "",
99 docstring = prepare_docstring(doc)
101 lines.extend(
103 docstring[0],
105 ":Type: ``%s``" % typ.__name__,
110 lines.extend(docstring[1:])
111 lines.append("")
113 return lines
116 def format_module(m):
117 lines = []
119 lines.extend(
121 ".. note::",
122 " moz.build files' implementation includes a ``Path`` class.",
125 path_docstring_minus_summary = prepare_docstring(m.Path.__doc__)[2:]
126 lines.extend([" " + line for line in path_docstring_minus_summary])
128 for subcontext, cls in sorted(m.SUBCONTEXTS.items()):
129 lines.extend(
131 ".. _mozbuild_subcontext_%s:" % subcontext,
133 "Sub-Context: %s" % subcontext,
134 "=============" + "=" * len(subcontext),
138 lines.extend(prepare_docstring(cls.__doc__))
139 if lines[-1]:
140 lines.append("")
142 for k, v in sorted(cls.VARIABLES.items()):
143 lines.extend(variable_reference(k, *v))
145 lines.extend(
147 "Variables",
148 "=========",
153 for v in sorted(m.VARIABLES):
154 lines.extend(variable_reference(v, *m.VARIABLES[v]))
156 lines.extend(
158 "Functions",
159 "=========",
164 for func in sorted(m.FUNCTIONS):
165 lines.extend(function_reference(func, *m.FUNCTIONS[func]))
167 lines.extend(
169 "Special Variables",
170 "=================",
175 for v in sorted(m.SPECIAL_VARIABLES):
176 lines.extend(special_reference(v, *m.SPECIAL_VARIABLES[v]))
178 return lines
181 def find_mots_config_path(app):
182 """Find and return mots config path if it exists."""
183 base_path = Path(app.srcdir).parent
184 config_path = base_path / "mots.yaml"
185 if config_path.exists():
186 return config_path
189 def export_mots(config_path):
190 """Load mots configuration and export it to file."""
191 # Load from disk and initialize configuration and directory.
192 config = FileConfig(config_path)
193 config.load()
194 directory = Directory(config)
195 directory.load()
197 # Fetch file format (i.e., "rst") and export path.
198 frmt = config.config["export"]["format"]
199 path = config_path.parent / config.config["export"]["path"]
201 # Generate output.
202 output = export_to_format(directory, frmt)
204 # Create export directory if it does not exist.
205 path.parent.mkdir(parents=True, exist_ok=True)
207 # Write changes to disk.
208 with path.open("w", encoding="utf-8") as f:
209 f.write(output)
212 class MozbuildSymbols(Directive):
213 """Directive to insert mozbuild sandbox symbol information."""
215 required_arguments = 1
217 def run(self):
218 module = importlib.import_module(self.arguments[0])
219 fname = module.__file__
220 if fname.endswith(".pyc"):
221 fname = fname[0:-1]
223 self.state.document.settings.record_dependencies.add(fname)
225 # We simply format out the documentation as rst then feed it back
226 # into the parser for conversion. We don't even emit ourselves, so
227 # there's no record of us.
228 self.state_machine.insert_input(format_module(module), fname)
230 return []
233 class Searchfox(ReferenceRole):
234 """Role which links a relative path from the source to it's searchfox URL.
236 Can be used like:
238 See :searchfox:`browser/base/content/browser-places.js` for more details.
240 Will generate a link to
241 ``https://searchfox.org/mozilla-central/source/browser/base/content/browser-places.js``
243 The example above will use the path as the text, to use custom text:
245 See :searchfox:`this file <browser/base/content/browser-places.js>` for
246 more details.
248 To specify a different source tree:
250 See :searchfox:`mozilla-beta:browser/base/content/browser-places.js`
251 for more details.
254 def run(self):
255 base = "https://searchfox.org/{source}/source/{path}"
257 if ":" in self.target:
258 source, path = self.target.split(":", 1)
259 else:
260 source = "mozilla-central"
261 path = self.target
263 url = base.format(source=source, path=path)
265 if self.has_explicit_title:
266 title = self.title
267 else:
268 title = path
270 node = nodes.reference(self.rawtext, title, refuri=url, **self.options)
271 return [node], []
274 def setup(app):
275 from moztreedocs import manager
277 app.add_directive("mozbuildsymbols", MozbuildSymbols)
278 app.add_role("searchfox", Searchfox())
280 # Unlike typical Sphinx installs, our documentation is assembled from
281 # many sources and staged in a common location. This arguably isn't a best
282 # practice, but it was the easiest to implement at the time.
284 # Here, we invoke our custom code for staging/generating all our
285 # documentation.
287 # Export and write "governance" documentation to disk.
288 config_path = find_mots_config_path(app)
289 if config_path:
290 export_mots(config_path)
292 manager.generate_docs(app)
293 app.srcdir = manager.staging_dir