Bug 1454640 - [docs] Always build docs with the tools/docs/conf.py r=mshal
authorAndrew Halberstadt <ahalberstadt@mozilla.com>
Wed, 18 Apr 2018 17:18:11 -0400
changeset 417798 155a531ddcdd4cf2e1ae4f7ad1fd0e44808a2b51
parent 417797 1381b6cea12d903b6154a6c09a4cc0603984b95e
child 417799 a71038f16918df5b43dd9813f238b23fd703b6ba
push id33980
push userebalazs@mozilla.com
push dateFri, 11 May 2018 09:35:12 +0000
treeherdermozilla-central@8e9a4a323f0c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1454640 - [docs] Always build docs with the tools/docs/conf.py r=mshal Previously, running |mach doc <subtree>| would use whatever conf.py file happened to live in the subtree. For example, running: ./mach doc tools/lint Would build with tools/lint/docs/conf.py. This is bad because it means the generated docs will look different from the docs that eventually will be published to firefox-source-docs.mozilla.com. This patch makes sure we always use tools/docs/conf.py for building, even when only generating a subtree. Furthermore, this sets things up such that when you modify a file, only the subtree containing the modified file will be re-generated. This cuts down rebuild times from ~2 minutes to ~20 seconds. There is one caveat. When rebuilding a subtree, the index of other trees will be overwritten in that particular subtree. I couldn't figure out anyway around this. This tradeoff for *much* faster rebuild times seems worth it. MozReview-Commit-ID: Ly88mvHKpo7
--- a/tools/docs/mach_commands.py
+++ b/tools/docs/mach_commands.py
@@ -19,16 +19,21 @@ from mozbuild.base import MachCommandBas
 here = os.path.abspath(os.path.dirname(__file__))
 class Documentation(MachCommandBase):
     """Helps manage in-tree documentation."""
+    def __init__(self, context):
+        super(Documentation, self).__init__(context)
+        self._manager = None
     @Command('doc', category='devenv',
              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', dest='fmt',
                      help='Documentation format to write.')
     @CommandArgument('--outdir', default=None, metavar='DESTINATION',
                      help='Where to write output.')
@@ -55,31 +60,28 @@ class Documentation(MachCommandBase):
             os.path.join(here, 'requirements.txt'), quiet=True)
         import webbrowser
         from livereload import Server
         from moztreedocs.package import create_tarball
         outdir = outdir or os.path.join(self.topobjdir, 'docs')
-        format_outdir = os.path.join(outdir, fmt)
+        savedir = 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'])
-        run_sphinx = partial(self._run_sphinx, docdir, savedir, fmt)
-        result = run_sphinx()
+        result = self._run_sphinx(docdir, savedir, fmt=fmt)
         if result != 0:
             return die('failed to generate documentation:\n'
                        '%s: sphinx return code %d' % (path, result))
             print('\nGenerated documentation:\n%s' % savedir)
         if archive:
             archive_path = os.path.join(outdir,
@@ -100,53 +102,72 @@ class Documentation(MachCommandBase):
         # will cause a re-build and refresh of the browser (if open).
             host, port = http.split(':', 1)
             port = int(port)
         except ValueError:
             return die('invalid address: %s' % http)
         server = Server()
-        server.watch(docdir, run_sphinx)
+        sphinx_trees = self.manager.trees or {savedir: docdir}
+        for dest, src in sphinx_trees.items():
+            run_sphinx = partial(self._run_sphinx, src, savedir, fmt=fmt)
+            server.watch(src, run_sphinx)
         server.serve(host=host, port=port, root=savedir,
                      open_url_delay=0.1 if auto_open else None)
-    def _run_sphinx(self, docdir, savedir, fmt='html'):
+    def _run_sphinx(self, docdir, savedir, config=None, fmt='html'):
         import sphinx
+        config = config or self.manager.conf_py_path
         args = [
             '-b', fmt,
+            '-c', os.path.dirname(config),
         return sphinx.build_main(args)
+    @property
+    def manager(self):
+        if not self._manager:
+            from moztreedocs import manager
+            self._manager = manager
+        return self._manager
     def _project_properties(self, path):
         import imp
-        path = os.path.join(path, 'conf.py')
+        path = os.path.normpath(self.manager.conf_py_path)
         with open(path, 'r') as fh:
             conf = imp.load_module('doc_conf', fh, path,
                                    ('.py', 'r', imp.PY_SOURCE))
         # Prefer the Mozilla project name, falling back to Sphinx's
         # default variable if it isn't defined.
         project = getattr(conf, 'moz_project_name', None)
         if not project:
             project = conf.project.replace(' ', '_')
         return {
             'project': project,
             'version': getattr(conf, 'version', None)
     def _find_doc_dir(self, path):
-        search_dirs = ('doc', 'docs')
-        for d in search_dirs:
+        if os.path.isfile(path):
+            return
+        valid_doc_dirs = ('doc', 'docs')
+        if os.path.basename(path) in valid_doc_dirs:
+            return path
+        for d in valid_doc_dirs:
             p = os.path.join(path, d)
-            if os.path.isfile(os.path.join(p, 'conf.py')):
+            if os.path.isdir(p):
                 return p
     def _s3_upload(self, root, project, version=None):
         from moztreedocs.package import distribution_files
         from moztreedocs.upload import s3_upload