diff options
Diffstat (limited to 'tools/docs/moztreedocs/__init__.py')
-rw-r--r-- | tools/docs/moztreedocs/__init__.py | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/tools/docs/moztreedocs/__init__.py b/tools/docs/moztreedocs/__init__.py new file mode 100644 index 000000000..a67edbded --- /dev/null +++ b/tools/docs/moztreedocs/__init__.py @@ -0,0 +1,126 @@ +# 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/. + +from __future__ import unicode_literals + +import os + +from mozbuild.frontend.reader import BuildReader +from mozpack.copier import FileCopier +from mozpack.files import FileFinder +from mozpack.manifests import InstallManifest + +import sphinx +import sphinx.apidoc + + +class SphinxManager(object): + """Manages the generation of Sphinx documentation for the tree.""" + + def __init__(self, topsrcdir, main_path, output_dir): + self._topsrcdir = topsrcdir + self._output_dir = output_dir + self._docs_dir = os.path.join(output_dir, '_staging') + self._conf_py_path = os.path.join(main_path, 'conf.py') + self._index_path = os.path.join(main_path, 'index.rst') + self._trees = {} + self._python_package_dirs = set() + + def read_build_config(self): + """Read the active build config and add docs to this instance.""" + + # Reading the Sphinx variables doesn't require a full build context. + # Only define the parts we need. + class fakeconfig(object): + def __init__(self, topsrcdir): + self.topsrcdir = topsrcdir + + config = fakeconfig(self._topsrcdir) + reader = BuildReader(config) + + for path, name, key, value in reader.find_sphinx_variables(): + reldir = os.path.dirname(path) + + if name == 'SPHINX_TREES': + assert key + self.add_tree(os.path.join(reldir, value), + os.path.join(reldir, key)) + + if name == 'SPHINX_PYTHON_PACKAGE_DIRS': + self.add_python_package_dir(os.path.join(reldir, value)) + + def add_tree(self, source_dir, dest_dir): + """Add a directory from where docs should be sourced.""" + if dest_dir in self._trees: + raise Exception('%s has already been registered as a destination.' + % dest_dir) + + self._trees[dest_dir] = source_dir + + def add_python_package_dir(self, source_dir): + """Add a directory containing Python packages. + + Added directories will have Python API docs generated automatically. + """ + self._python_package_dirs.add(source_dir) + + def generate_docs(self, app): + """Generate/stage documentation.""" + app.info('Reading Sphinx metadata from build configuration') + self.read_build_config() + app.info('Staging static documentation') + self._synchronize_docs() + app.info('Generating Python API documentation') + self._generate_python_api_docs() + + def _generate_python_api_docs(self): + """Generate Python API doc files.""" + out_dir = os.path.join(self._docs_dir, 'python') + base_args = ['sphinx', '--no-toc', '-o', out_dir] + + for p in sorted(self._python_package_dirs): + full = os.path.join(self._topsrcdir, p) + + finder = FileFinder(full, find_executables=False) + dirs = {os.path.dirname(f[0]) for f in finder.find('**')} + + excludes = {d for d in dirs if d.endswith('test')} + + args = list(base_args) + args.append(full) + args.extend(excludes) + + sphinx.apidoc.main(args) + + def _synchronize_docs(self): + m = InstallManifest() + + m.add_symlink(self._conf_py_path, 'conf.py') + + for dest, source in sorted(self._trees.items()): + source_dir = os.path.join(self._topsrcdir, source) + for root, dirs, files in os.walk(source_dir): + for f in files: + source_path = os.path.join(root, f) + rel_source = source_path[len(source_dir) + 1:] + + m.add_symlink(source_path, os.path.join(dest, rel_source)) + + copier = FileCopier() + m.populate_registry(copier) + copier.copy(self._docs_dir) + + with open(self._index_path, 'rb') as fh: + data = fh.read() + + indexes = ['%s/index' % p for p in sorted(self._trees.keys())] + indexes = '\n '.join(indexes) + + packages = [os.path.basename(p) for p in self._python_package_dirs] + packages = ['python/%s' % p for p in packages] + packages = '\n '.join(sorted(packages)) + data = data.format(indexes=indexes, python_packages=packages) + + with open(os.path.join(self._docs_dir, 'index.rst'), 'wb') as fh: + fh.write(data) |