summaryrefslogtreecommitdiffstats
path: root/python/mozbuild/mozbuild/sphinx.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/mozbuild/mozbuild/sphinx.py')
-rw-r--r--python/mozbuild/mozbuild/sphinx.py293
1 files changed, 293 insertions, 0 deletions
diff --git a/python/mozbuild/mozbuild/sphinx.py b/python/mozbuild/mozbuild/sphinx.py
new file mode 100644
index 0000000000..4d7afb621c
--- /dev/null
+++ b/python/mozbuild/mozbuild/sphinx.py
@@ -0,0 +1,293 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import importlib
+from pathlib import Path
+
+from docutils import nodes
+from docutils.parsers.rst import Directive
+from mots.config import FileConfig
+from mots.directory import Directory
+from mots.export import export_to_format
+from sphinx.util.docstrings import prepare_docstring
+from sphinx.util.docutils import ReferenceRole
+
+
+def function_reference(f, attr, args, doc):
+ lines = []
+
+ lines.extend(
+ [
+ f,
+ "-" * len(f),
+ "",
+ ]
+ )
+
+ docstring = prepare_docstring(doc)
+
+ lines.extend(
+ [
+ docstring[0],
+ "",
+ ]
+ )
+
+ arg_types = []
+
+ for t in args:
+ if isinstance(t, list):
+ inner_types = [t2.__name__ for t2 in t]
+ arg_types.append(" | ".join(inner_types))
+ continue
+
+ arg_types.append(t.__name__)
+
+ arg_s = "(%s)" % ", ".join(arg_types)
+
+ lines.extend(
+ [
+ ":Arguments: %s" % arg_s,
+ "",
+ ]
+ )
+
+ lines.extend(docstring[1:])
+ lines.append("")
+
+ return lines
+
+
+def variable_reference(v, st_type, in_type, doc):
+ lines = [
+ v,
+ "-" * len(v),
+ "",
+ ]
+
+ docstring = prepare_docstring(doc)
+
+ lines.extend(
+ [
+ docstring[0],
+ "",
+ ]
+ )
+
+ lines.extend(
+ [
+ ":Storage Type: ``%s``" % st_type.__name__,
+ ":Input Type: ``%s``" % in_type.__name__,
+ "",
+ ]
+ )
+
+ lines.extend(docstring[1:])
+ lines.append("")
+
+ return lines
+
+
+def special_reference(v, func, typ, doc):
+ lines = [
+ v,
+ "-" * len(v),
+ "",
+ ]
+
+ docstring = prepare_docstring(doc)
+
+ lines.extend(
+ [
+ docstring[0],
+ "",
+ ":Type: ``%s``" % typ.__name__,
+ "",
+ ]
+ )
+
+ lines.extend(docstring[1:])
+ lines.append("")
+
+ return lines
+
+
+def format_module(m):
+ lines = []
+
+ lines.extend(
+ [
+ ".. note::",
+ " moz.build files' implementation includes a ``Path`` class.",
+ ]
+ )
+ path_docstring_minus_summary = prepare_docstring(m.Path.__doc__)[2:]
+ lines.extend([" " + line for line in path_docstring_minus_summary])
+
+ for subcontext, cls in sorted(m.SUBCONTEXTS.items()):
+ lines.extend(
+ [
+ ".. _mozbuild_subcontext_%s:" % subcontext,
+ "",
+ "Sub-Context: %s" % subcontext,
+ "=============" + "=" * len(subcontext),
+ "",
+ ]
+ )
+ lines.extend(prepare_docstring(cls.__doc__))
+ if lines[-1]:
+ lines.append("")
+
+ for k, v in sorted(cls.VARIABLES.items()):
+ lines.extend(variable_reference(k, *v))
+
+ lines.extend(
+ [
+ "Variables",
+ "=========",
+ "",
+ ]
+ )
+
+ for v in sorted(m.VARIABLES):
+ lines.extend(variable_reference(v, *m.VARIABLES[v]))
+
+ lines.extend(
+ [
+ "Functions",
+ "=========",
+ "",
+ ]
+ )
+
+ for func in sorted(m.FUNCTIONS):
+ lines.extend(function_reference(func, *m.FUNCTIONS[func]))
+
+ lines.extend(
+ [
+ "Special Variables",
+ "=================",
+ "",
+ ]
+ )
+
+ for v in sorted(m.SPECIAL_VARIABLES):
+ lines.extend(special_reference(v, *m.SPECIAL_VARIABLES[v]))
+
+ return lines
+
+
+def find_mots_config_path(app):
+ """Find and return mots config path if it exists."""
+ base_path = Path(app.srcdir).parent
+ config_path = base_path / "mots.yaml"
+ if config_path.exists():
+ return config_path
+
+
+def export_mots(config_path):
+ """Load mots configuration and export it to file."""
+ # Load from disk and initialize configuration and directory.
+ config = FileConfig(config_path)
+ config.load()
+ directory = Directory(config)
+ directory.load()
+
+ # Fetch file format (i.e., "rst") and export path.
+ frmt = config.config["export"]["format"]
+ path = config_path.parent / config.config["export"]["path"]
+
+ # Generate output.
+ output = export_to_format(directory, frmt)
+
+ # Create export directory if it does not exist.
+ path.parent.mkdir(parents=True, exist_ok=True)
+
+ # Write changes to disk.
+ with path.open("w", encoding="utf-8") as f:
+ f.write(output)
+
+
+class MozbuildSymbols(Directive):
+ """Directive to insert mozbuild sandbox symbol information."""
+
+ required_arguments = 1
+
+ def run(self):
+ module = importlib.import_module(self.arguments[0])
+ fname = module.__file__
+ if fname.endswith(".pyc"):
+ fname = fname[0:-1]
+
+ self.state.document.settings.record_dependencies.add(fname)
+
+ # We simply format out the documentation as rst then feed it back
+ # into the parser for conversion. We don't even emit ourselves, so
+ # there's no record of us.
+ self.state_machine.insert_input(format_module(module), fname)
+
+ return []
+
+
+class Searchfox(ReferenceRole):
+ """Role which links a relative path from the source to it's searchfox URL.
+
+ Can be used like:
+
+ See :searchfox:`browser/base/content/browser-places.js` for more details.
+
+ Will generate a link to
+ ``https://searchfox.org/mozilla-central/source/browser/base/content/browser-places.js``
+
+ The example above will use the path as the text, to use custom text:
+
+ See :searchfox:`this file <browser/base/content/browser-places.js>` for
+ more details.
+
+ To specify a different source tree:
+
+ See :searchfox:`mozilla-beta:browser/base/content/browser-places.js`
+ for more details.
+ """
+
+ def run(self):
+ base = "https://searchfox.org/{source}/source/{path}"
+
+ if ":" in self.target:
+ source, path = self.target.split(":", 1)
+ else:
+ source = "mozilla-central"
+ path = self.target
+
+ url = base.format(source=source, path=path)
+
+ if self.has_explicit_title:
+ title = self.title
+ else:
+ title = path
+
+ node = nodes.reference(self.rawtext, title, refuri=url, **self.options)
+ return [node], []
+
+
+def setup(app):
+ from moztreedocs import manager
+
+ app.add_directive("mozbuildsymbols", MozbuildSymbols)
+ app.add_role("searchfox", Searchfox())
+
+ # Unlike typical Sphinx installs, our documentation is assembled from
+ # many sources and staged in a common location. This arguably isn't a best
+ # practice, but it was the easiest to implement at the time.
+ #
+ # Here, we invoke our custom code for staging/generating all our
+ # documentation.
+
+ # Export and write "governance" documentation to disk.
+ config_path = find_mots_config_path(app)
+ if config_path:
+ export_mots(config_path)
+
+ manager.generate_docs(app)
+ app.srcdir = manager.staging_dir