summaryrefslogtreecommitdiffstats
path: root/tools/docs
diff options
context:
space:
mode:
Diffstat (limited to 'tools/docs')
-rw-r--r--tools/docs/Vagrantfile13
-rw-r--r--tools/docs/conf.py83
-rw-r--r--tools/docs/index.rst58
-rw-r--r--tools/docs/mach_commands.py117
-rw-r--r--tools/docs/moztreedocs/__init__.py126
5 files changed, 397 insertions, 0 deletions
diff --git a/tools/docs/Vagrantfile b/tools/docs/Vagrantfile
new file mode 100644
index 000000000..247afe1b3
--- /dev/null
+++ b/tools/docs/Vagrantfile
@@ -0,0 +1,13 @@
+# -*- mode: ruby -*-
+# vi: set ft=ruby :
+
+# We intentionally use the old config format because Mozilla's Jenkins
+# server doesn't run a modern Vagrant.
+Vagrant::Config.run do |config|
+ config.vm.box = "precise64"
+ config.vm.box_url = "http://files.vagrantup.com/precise64.box"
+ config.vm.share_folder("gecko", "/gecko", "../..")
+ # Doxygen needs more than the default memory or it will swap and be
+ # extremely slow.
+ config.vm.customize ["modifyvm", :id, "--memory", 2048]
+end
diff --git a/tools/docs/conf.py b/tools/docs/conf.py
new file mode 100644
index 000000000..38cea035a
--- /dev/null
+++ b/tools/docs/conf.py
@@ -0,0 +1,83 @@
+# 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
+import re
+import sys
+
+from datetime import datetime
+
+# Set up Python environment to load build system packages.
+OUR_DIR = os.path.dirname(__file__)
+topsrcdir = os.path.normpath(os.path.join(OUR_DIR, '..', '..'))
+
+EXTRA_PATHS = (
+ 'layout/tools/reftest',
+ 'python/futures',
+ 'python/jsmin',
+ 'python/mach',
+ 'python/mozbuild',
+ 'python/mozversioncontrol',
+ 'python/which',
+ 'testing/mozbase/manifestparser',
+ 'testing/mozbase/mozfile',
+ 'testing/mozbase/mozprocess',
+)
+
+sys.path[:0] = [os.path.join(topsrcdir, p) for p in EXTRA_PATHS]
+
+sys.path.insert(0, OUR_DIR)
+
+extensions = [
+ 'sphinx.ext.autodoc',
+ 'sphinx.ext.graphviz',
+ 'sphinx.ext.todo',
+ 'mozbuild.sphinx',
+]
+
+templates_path = ['_templates']
+source_suffix = '.rst'
+master_doc = 'index'
+project = u'Mozilla Source Tree Docs'
+year = datetime.now().year
+
+# Grab the version from the source tree's milestone.
+# FUTURE Use Python API from bug 941299.
+with open(os.path.join(topsrcdir, 'config', 'milestone.txt'), 'rt') as fh:
+ for line in fh:
+ line = line.strip()
+
+ if not line or line.startswith('#'):
+ continue
+
+ release = line
+ break
+
+version = re.sub(r'[ab]\d+$', '', release)
+
+exclude_patterns = ['_build', '_staging', '_venv']
+pygments_style = 'sphinx'
+
+# We need to perform some adjustment of the settings and environment
+# when running on Read The Docs.
+on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
+
+if on_rtd:
+ # SHELL isn't set on RTD and mach.mixin.process's import raises if a
+ # shell-related environment variable can't be found. Set the variable here
+ # to hack us into working on RTD.
+ assert 'SHELL' not in os.environ
+ os.environ['SHELL'] = '/bin/bash'
+else:
+ # We only need to set the RTD theme when not on RTD because the RTD
+ # environment handles this otherwise.
+ import sphinx_rtd_theme
+ html_theme = 'sphinx_rtd_theme'
+ html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
+
+
+html_static_path = ['_static']
+htmlhelp_basename = 'MozillaTreeDocs'
diff --git a/tools/docs/index.rst b/tools/docs/index.rst
new file mode 100644
index 000000000..ae2a9d593
--- /dev/null
+++ b/tools/docs/index.rst
@@ -0,0 +1,58 @@
+=================================
+Mozilla Source Tree Documentation
+=================================
+
+.. toctree::
+ :maxdepth: 1
+
+ {indexes}
+
+Python Packages
+===============
+
+.. toctree::
+ :maxdepth: 2
+
+ {python_packages}
+
+Managing Documentation
+======================
+
+This documentation is generated via the
+`Sphinx <http://sphinx-doc.org/>`_ tool from sources in the tree.
+
+To build the documentation, run ``mach doc``. Run
+``mach help doc`` to see configurable options.
+
+Adding Documentation
+--------------------
+
+To add new documentation, define the ``SPHINX_TREES`` and
+``SPHINX_PYTHON_PACKAGE_DIRS`` variables in ``moz.build`` files in
+the tree and documentation will automatically get picked up.
+
+Say you have a directory ``featureX`` you would like to write some
+documentation for. Here are the steps to create Sphinx documentation
+for it:
+
+1. Create a directory for the docs. This is typically ``docs``. e.g.
+ ``featureX/docs``.
+2. Create an ``index.rst`` file in this directory. The ``index.rst`` file
+ is the root documentation for that section. See ``build/docs/index.rst``
+ for an example file.
+3. In a ``moz.build`` file (typically the one in the parent directory of
+ the ``docs`` directory), define ``SPHINX_TREES`` to hook up the plumbing.
+ e.g. ``SPHINX_TREES['featureX'] = 'docs'``. This says *the ``docs``
+ directory under the current directory should be installed into the
+ Sphinx documentation tree under ``/featureX``*.
+4. If you have Python packages you would like to generate Python API
+ documentation for, you can use ``SPHINX_PYTHON_PACKAGE_DIRS`` to
+ declare directories containing Python packages. e.g.
+ ``SPHINX_PYTHON_PACKAGE_DIRS += ['mozpackage']``.
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/tools/docs/mach_commands.py b/tools/docs/mach_commands.py
new file mode 100644
index 000000000..825e5ec38
--- /dev/null
+++ b/tools/docs/mach_commands.py
@@ -0,0 +1,117 @@
+# 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 absolute_import, print_function, unicode_literals
+
+import os
+import sys
+
+from mach.decorators import (
+ Command,
+ CommandArgument,
+ CommandProvider,
+)
+
+import mozhttpd
+
+from mozbuild.base import MachCommandBase
+
+
+@CommandProvider
+class Documentation(MachCommandBase):
+ """Helps manage in-tree documentation."""
+
+ @Command('doc', category='devenv',
+ description='Generate and display documentation from the tree.')
+ @CommandArgument('what', nargs='*', metavar='DIRECTORY [, DIRECTORY]',
+ help='Path(s) to documentation to build and display.')
+ @CommandArgument('--format', default='html',
+ help='Documentation format to write.')
+ @CommandArgument('--outdir', default=None, metavar='DESTINATION',
+ help='Where to write output.')
+ @CommandArgument('--no-open', dest='auto_open', default=True, action='store_false',
+ help="Don't automatically open HTML docs in a browser.")
+ @CommandArgument('--http', const=':6666', metavar='ADDRESS', nargs='?',
+ help='Serve documentation on an HTTP server, e.g. ":6666".')
+ def build_docs(self, what=None, format=None, outdir=None, auto_open=True, http=None):
+ self._activate_virtualenv()
+ self.virtualenv_manager.install_pip_package('sphinx_rtd_theme==0.1.6')
+
+ import sphinx
+ import webbrowser
+
+ if not outdir:
+ outdir = os.path.join(self.topobjdir, 'docs')
+ if not what:
+ what = [os.path.join(self.topsrcdir, 'tools')]
+ outdir = os.path.join(outdir, format)
+
+ generated = []
+ failed = []
+ for path in what:
+ path = os.path.normpath(os.path.abspath(path))
+ docdir = self._find_doc_dir(path)
+
+ if not docdir:
+ failed.append((path, 'could not find docs at this location'))
+ continue
+
+ # find project name to use as a namespace within `outdir`
+ project = self._find_project_name(docdir)
+ savedir = os.path.join(outdir, project)
+
+ args = [
+ 'sphinx',
+ '-b', format,
+ docdir,
+ savedir,
+ ]
+ result = sphinx.build_main(args)
+ if result != 0:
+ failed.append((path, 'sphinx return code %d' % result))
+ else:
+ generated.append(savedir)
+
+ index_path = os.path.join(savedir, 'index.html')
+ if not http and auto_open and os.path.isfile(index_path):
+ webbrowser.open(index_path)
+
+ if generated:
+ print('\nGenerated documentation:\n%s\n' % '\n'.join(generated))
+
+ if failed:
+ failed = ['%s: %s' % (f[0], f[1]) for f in failed]
+ return die('failed to generate documentation:\n%s' % '\n'.join(failed))
+
+ if http is not None:
+ host, port = http.split(':', 1)
+ addr = (host, int(port))
+ if len(addr) != 2:
+ return die('invalid address: %s' % http)
+
+ httpd = mozhttpd.MozHttpd(host=addr[0], port=addr[1], docroot=outdir)
+ print('listening on %s:%d' % addr)
+ httpd.start(block=True)
+
+ def _find_project_name(self, path):
+ import imp
+ path = os.path.join(path, 'conf.py')
+ with open(path, 'r') as fh:
+ conf = imp.load_module('doc_conf', fh, path,
+ ('.py', 'r', imp.PY_SOURCE))
+
+ return conf.project.replace(' ', '_')
+
+ def _find_doc_dir(self, path):
+ search_dirs = ('doc', 'docs')
+ for d in search_dirs:
+ p = os.path.join(path, d)
+ if os.path.isfile(os.path.join(p, 'conf.py')):
+ return p
+
+
+def die(msg, exit_code=1):
+ msg = '%s: %s' % (sys.argv[0], msg)
+ print(msg, file=sys.stderr)
+ return exit_code
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)