Bug 1454640 - [docs] Use a single SphinxManager instance across all rebuilds
In the mozbuild.sphinx extension, we create a new SphinxManager instance each
time. However this isn't ideal now that we can rebuild the docs within the same
interpreter using the livereload server.
This makes use of a singleton so that we can share state not only between
multiple invocations of sphinx-build, but also with the mach command. This will
be taken advantage of more heavily in future commits in this series.
MozReview-Commit-ID: 7ERYeN5BPeI
--- a/python/mozbuild/mozbuild/sphinx.py
+++ b/python/mozbuild/mozbuild/sphinx.py
@@ -164,37 +164,32 @@ class MozbuildSymbols(Directive):
# 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 []
def setup(app):
+ from mozbuild.virtualenv import VirtualenvManager
+ from moztreedocs import manager
+
app.add_directive('mozbuildsymbols', MozbuildSymbols)
# 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.
- from moztreedocs import SphinxManager
-
- topsrcdir = app.config._raw_config['topsrcdir']
- manager = SphinxManager(topsrcdir,
- os.path.join(topsrcdir, 'tools', 'docs'),
- app.outdir)
manager.generate_docs(app)
-
- app.srcdir = os.path.join(app.outdir, '_staging')
+ app.srcdir = manager.staging_dir
# We need to adjust sys.path in order for Python API docs to get generated
# properly. We leverage the in-tree virtualenv for this.
- from mozbuild.virtualenv import VirtualenvManager
-
+ topsrcdir = manager.topsrcdir
ve = VirtualenvManager(topsrcdir,
os.path.join(topsrcdir, 'dummy-objdir'),
os.path.join(app.outdir, '_venv'),
sys.stderr,
os.path.join(topsrcdir, 'build', 'virtualenv_packages.txt'))
ve.ensure()
ve.activate()
--- a/tools/docs/moztreedocs/__init__.py
+++ b/tools/docs/moztreedocs/__init__.py
@@ -1,52 +1,59 @@
# 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.base import MozbuildObject
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
+here = os.path.abspath(os.path.dirname(__file__))
+build = MozbuildObject.from_environment(cwd=here)
-class SphinxManager(object):
+MAIN_DOC_PATH = os.path.join(build.topsrcdir, 'tools', 'docs')
+
+
+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')
+ def __init__(self, topsrcdir, main_path):
+ self.topsrcdir = topsrcdir
+ 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()
+ # Instance variables that get set in self.generate_docs()
+ self.staging_dir = None
+
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)
+ 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
if key.startswith('/'):
key = key[1:]
else:
key = os.path.join(reldir, key)
self.add_tree(os.path.join(reldir, value), key)
@@ -65,65 +72,70 @@ class SphinxManager(object):
"""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."""
+ self.staging_dir = os.path.join(app.outdir, '_staging')
+
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')
+ out_dir = os.path.join(self.staging_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)
+ full = os.path.join(self.topsrcdir, p)
finder = FileFinder(full)
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_link(self._conf_py_path, 'conf.py')
+ m.add_link(self.conf_py_path, 'conf.py')
for dest, source in sorted(self._trees.items()):
- source_dir = os.path.join(self._topsrcdir, source)
+ 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_link(source_path, os.path.join(dest, rel_source))
copier = FileCopier()
m.populate_registry(copier)
- copier.copy(self._docs_dir)
+ copier.copy(self.staging_dir)
- with open(self._index_path, 'rb') as fh:
+ 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:
+ with open(os.path.join(self.staging_dir, 'index.rst'), 'wb') as fh:
fh.write(data)
+
+
+manager = _SphinxManager(build.topsrcdir, MAIN_DOC_PATH)