michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, # You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: from __future__ import unicode_literals michael@0: michael@0: import os michael@0: michael@0: from mozpack.copier import FileCopier michael@0: from mozpack.files import FileFinder michael@0: from mozpack.manifests import InstallManifest michael@0: michael@0: import sphinx michael@0: import sphinx.apidoc michael@0: michael@0: michael@0: class SphinxManager(object): michael@0: """Manages the generation of Sphinx documentation for the tree.""" michael@0: michael@0: def __init__(self, topsrcdir, main_path, output_dir): michael@0: self._topsrcdir = topsrcdir michael@0: self._output_dir = output_dir michael@0: self._conf_py_path = os.path.join(main_path, 'conf.py') michael@0: self._index_path = os.path.join(main_path, 'index.rst') michael@0: self._trees = {} michael@0: self._python_package_dirs = set() michael@0: michael@0: def add_tree(self, source_dir, dest_dir): michael@0: """Add a directory from where docs should be sourced.""" michael@0: if dest_dir in self._trees: michael@0: raise Exception('%s has already been registered as a destination.' michael@0: % dest_dir) michael@0: michael@0: self._trees[dest_dir] = source_dir michael@0: michael@0: def add_python_package_dir(self, source_dir): michael@0: """Add a directory containing Python packages. michael@0: michael@0: Added directories will have Python API docs generated automatically. michael@0: """ michael@0: self._python_package_dirs.add(source_dir) michael@0: michael@0: def generate_docs(self, fmt): michael@0: """Generate documentation using Sphinx.""" michael@0: self._synchronize_docs() michael@0: self._generate_python_api_docs() michael@0: michael@0: old_env = os.environ.copy() michael@0: try: michael@0: os.environ['MOZILLA_DIR'] = self._topsrcdir michael@0: args = [ michael@0: 'sphinx', michael@0: '-b', fmt, michael@0: os.path.join(self._output_dir, 'staging'), michael@0: os.path.join(self._output_dir, fmt), michael@0: ] michael@0: michael@0: return sphinx.main(args) michael@0: finally: michael@0: os.environ.clear() michael@0: os.environ.update(old_env) michael@0: michael@0: def _generate_python_api_docs(self): michael@0: """Generate Python API doc files.""" michael@0: out_dir = os.path.join(self._output_dir, 'staging', 'python') michael@0: base_args = ['sphinx', '--no-toc', '-o', out_dir] michael@0: michael@0: for p in sorted(self._python_package_dirs): michael@0: full = os.path.join(self._topsrcdir, p) michael@0: michael@0: finder = FileFinder(full, find_executables=False) michael@0: dirs = {os.path.dirname(f[0]) for f in finder.find('**')} michael@0: michael@0: excludes = {d for d in dirs if d.endswith('test')} michael@0: michael@0: args = list(base_args) michael@0: args.append(full) michael@0: args.extend(excludes) michael@0: michael@0: sphinx.apidoc.main(args) michael@0: michael@0: def _synchronize_docs(self): michael@0: m = InstallManifest() michael@0: michael@0: m.add_symlink(self._conf_py_path, 'conf.py') michael@0: michael@0: for dest, source in sorted(self._trees.items()): michael@0: source_dir = os.path.join(self._topsrcdir, source) michael@0: for root, dirs, files in os.walk(source_dir): michael@0: for f in files: michael@0: source_path = os.path.join(root, f) michael@0: rel_source = source_path[len(source_dir) + 1:] michael@0: michael@0: m.add_symlink(source_path, os.path.join(dest, rel_source)) michael@0: michael@0: stage_dir = os.path.join(self._output_dir, 'staging') michael@0: copier = FileCopier() michael@0: m.populate_registry(copier) michael@0: copier.copy(stage_dir) michael@0: michael@0: with open(self._index_path, 'rb') as fh: michael@0: data = fh.read() michael@0: michael@0: indexes = ['%s/index' % p for p in sorted(self._trees.keys())] michael@0: indexes = '\n '.join(indexes) michael@0: michael@0: packages = [os.path.basename(p) for p in self._python_package_dirs] michael@0: packages = ['python/%s' % p for p in packages] michael@0: packages = '\n '.join(sorted(packages)) michael@0: data = data.format(indexes=indexes, python_packages=packages) michael@0: michael@0: with open(os.path.join(stage_dir, 'index.rst'), 'wb') as fh: michael@0: fh.write(data)