fune/tools/docs/moztreedocs/__init__.py
Tom Prince c26f79ab2a Bug 1402154: Allow specifying an absolute path to mount sphinx docs. r=gps
MozReview-Commit-ID: 8OLCtwg8zXc

--HG--
extra : rebase_source : ad2ab3272d8f326f80287b190e779366872d95fc
2017-11-20 11:56:52 -07:00

150 lines
5.2 KiB
Python

# 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.archive import create_tar_gz_from_files
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
if key.startswith('/'):
key = key[1:]
else:
key = os.path.join(reldir, key)
self.add_tree(os.path.join(reldir, value), 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)
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')
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_link(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)
def distribution_files(root):
"""Find all files suitable for distributing.
Given the path to generated Sphinx documentation, returns an iterable
of (path, BaseFile) for files that should be archived, uploaded, etc.
Paths are relative to given root directory.
"""
finder = FileFinder(root, ignore=('_staging', '_venv'))
return finder.find('**')
def create_tarball(filename, root):
"""Create a tar.gz archive of docs in a directory."""
files = dict(distribution_files(root))
with open(filename, 'wb') as fh:
create_tar_gz_from_files(fh, files, filename=os.path.basename(filename),
compresslevel=6)