--- a/tools/docs/mach_commands.py
+++ b/tools/docs/mach_commands.py
@@ -1,118 +1,128 @@
# 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 functools import partial
from mach.decorators import (
Command,
CommandArgument,
CommandProvider,
)
import which
-import mozhttpd
-
from mozbuild.base import MachCommandBase
here = os.path.abspath(os.path.dirname(__file__))
@CommandProvider
class Documentation(MachCommandBase):
"""Helps manage in-tree documentation."""
@Command('doc', category='devenv',
- description='Generate and display documentation from the tree.')
+ description='Generate and serve documentation from the tree.')
@CommandArgument('path', default=None, metavar='DIRECTORY', nargs='?',
help='Path to documentation to build and display.')
- @CommandArgument('--format', default='html',
+ @CommandArgument('--format', default='html', dest='fmt',
help='Documentation format to write.')
@CommandArgument('--outdir', default=None, metavar='DESTINATION',
help='Where to write output.')
@CommandArgument('--archive', action='store_true',
- help='Write a gzipped tarball of generated docs')
+ help='Write a gzipped tarball of generated docs.')
@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".')
+ @CommandArgument('--no-serve', dest='serve', default=True, action='store_false',
+ help="Don't serve the generated docs after building.")
+ @CommandArgument('--http', default='localhost:5500', metavar='ADDRESS',
+ help='Serve documentation on the specified host and port, '
+ 'default "localhost:5500".')
@CommandArgument('--upload', action='store_true',
- help='Upload generated files to S3')
- def build_docs(self, path=None, format=None, outdir=None, auto_open=True,
- http=None, archive=False, upload=False):
+ help='Upload generated files to S3.')
+ def build_docs(self, path=None, fmt='html', outdir=None, auto_open=True,
+ serve=True, http=None, archive=False, upload=False):
try:
which.which('jsdoc')
except which.WhichError:
return die('jsdoc not found - please install from npm.')
self._activate_virtualenv()
self.virtualenv_manager.install_pip_requirements(
os.path.join(here, 'requirements.txt'), quiet=True)
- import sphinx
+ import moztreedocs
import webbrowser
- import moztreedocs
+ from livereload import Server
outdir = outdir or os.path.join(self.topobjdir, 'docs')
- format_outdir = os.path.join(outdir, format)
+ format_outdir = os.path.join(outdir, fmt)
path = path or os.path.join(self.topsrcdir, 'tools')
path = os.path.normpath(os.path.abspath(path))
docdir = self._find_doc_dir(path)
if not docdir:
return die('failed to generate documentation:\n'
'%s: could not find docs at this location' % path)
props = self._project_properties(docdir)
savedir = os.path.join(format_outdir, props['project'])
- args = [
- 'sphinx',
- '-b', format,
- docdir,
- savedir,
- ]
- result = sphinx.build_main(args)
+ run_sphinx = partial(self._run_sphinx, docdir, savedir, fmt)
+ result = run_sphinx()
if result != 0:
return die('failed to generate documentation:\n'
'%s: sphinx return code %d' % (path, result))
else:
print('\nGenerated documentation:\n%s' % savedir)
if archive:
archive_path = os.path.join(outdir,
'%s.tar.gz' % props['project'])
moztreedocs.create_tarball(archive_path, savedir)
print('Archived to %s' % archive_path)
if upload:
self._s3_upload(savedir, props['project'], props['version'])
- 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 not serve:
+ index_path = os.path.join(savedir, 'index.html')
+ if auto_open and os.path.isfile(index_path):
+ webbrowser.open(index_path)
+ return
- if http is not None:
+ # Create livereload server. Any files modified in the specified docdir
+ # will cause a re-build and refresh of the browser (if open).
+ try:
host, port = http.split(':', 1)
- addr = (host, int(port))
- if len(addr) != 2:
- return die('invalid address: %s' % http)
+ port = int(port)
+ except ValueError:
+ return die('invalid address: %s' % http)
+
+ server = Server()
+ server.watch(docdir, run_sphinx)
+ server.serve(host=host, port=port, root=savedir,
+ open_url_delay=0.1 if auto_open else None)
- httpd = mozhttpd.MozHttpd(host=addr[0], port=addr[1],
- docroot=format_outdir)
- print('listening on %s:%d' % addr)
- httpd.start(block=True)
+ def _run_sphinx(self, docdir, savedir, fmt='html'):
+ import sphinx
+ args = [
+ 'sphinx',
+ '-b', fmt,
+ docdir,
+ savedir,
+ ]
+ return sphinx.build_main(args)
def _project_properties(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))