Bug 1220789 - Generalize |mach build-doc| for any arbitrary sphinx projects; rename to |mach doc|, r=gps
authorAndrew Halberstadt <ahalberstadt@mozilla.com>
Tue, 03 Nov 2015 15:39:50 -0500
changeset 271567 bf3b50448aa1d9d3c7e48744a8d3dd918e27c8d0
parent 271566 d7b34dc6ef9e2f5e62b622a14e7c49ae5c388f3f
child 271568 c99a26fcff8f7ecb57b379e2bbe909c3844c2b69
push id67699
push userahalberstadt@mozilla.com
push dateFri, 06 Nov 2015 21:12:52 +0000
treeherdermozilla-inbound@bf3b50448aa1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgps
bugs1220789
milestone45.0a1
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 1220789 - Generalize |mach build-doc| for any arbitrary sphinx projects; rename to |mach doc|, r=gps Now, running |mach doc <path/to/project>| will generate the sphinx based docs of the project and open them in the default browser. Mulitple doc paths can be supplied at a time. E.g: ./mach doc testing/mozbase
tools/docs/mach_commands.py
--- a/tools/docs/mach_commands.py
+++ b/tools/docs/mach_commands.py
@@ -14,31 +14,85 @@ from mach.decorators import (
 
 from mozbuild.base import MachCommandBase
 
 
 @CommandProvider
 class Documentation(MachCommandBase):
     """Helps manage in-tree documentation."""
 
-    @Command('build-docs', category='build-dev',
-        description='Generate documentation for the tree.')
+    @Command('doc', category='devenv',
+        description='Generate and display documentation from the tree.')
+    @CommandArgument('what', default=None, nargs='*',
+        help='Path(s) to documentation to build and display.')
     @CommandArgument('--format', default='html',
         help='Documentation format to write.')
-    @CommandArgument('outdir', default='<DEFAULT>', nargs='?',
+    @CommandArgument('--outdir', default=None,
         help='Where to write output.')
-    def build_docs(self, format=None, outdir=None):
+    @CommandArgument('--no-open', dest='auto_open', default=True, action='store_false',
+        help='Don\'t automatically open HTML docs in a browser.')
+    def build_docs(self, what=None, format=None, outdir=None, auto_open=True):
         self._activate_virtualenv()
         self.virtualenv_manager.install_pip_package('sphinx_rtd_theme==0.1.6')
 
         import sphinx
+        import webbrowser
 
-        if outdir == '<DEFAULT>':
+        if not outdir:
             outdir = os.path.join(self.topobjdir, 'docs')
 
-        args = [
-            'sphinx',
-            '-b', format,
-            os.path.join(self.topsrcdir, 'tools', 'docs'),
-            os.path.join(outdir, format),
-        ]
+        if not what:
+            what = [os.path.join(self.topsrcdir, 'tools')]
+
+        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, format, 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)
 
-        return sphinx.main(args)
+            # attempt to open html docs in a browser
+            index_path = os.path.join(savedir, 'index.html')
+            if 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]
+            print("ERROR failed to generate documentation:\n%s" % '\n'.join(failed))
+            return 1
+
+    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