|
1 # This Source Code Form is subject to the terms of the Mozilla Public |
|
2 # License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 # file, # You can obtain one at http://mozilla.org/MPL/2.0/. |
|
4 |
|
5 from __future__ import unicode_literals |
|
6 |
|
7 import os |
|
8 |
|
9 from mozpack.copier import FileCopier |
|
10 from mozpack.files import FileFinder |
|
11 from mozpack.manifests import InstallManifest |
|
12 |
|
13 import sphinx |
|
14 import sphinx.apidoc |
|
15 |
|
16 |
|
17 class SphinxManager(object): |
|
18 """Manages the generation of Sphinx documentation for the tree.""" |
|
19 |
|
20 def __init__(self, topsrcdir, main_path, output_dir): |
|
21 self._topsrcdir = topsrcdir |
|
22 self._output_dir = output_dir |
|
23 self._conf_py_path = os.path.join(main_path, 'conf.py') |
|
24 self._index_path = os.path.join(main_path, 'index.rst') |
|
25 self._trees = {} |
|
26 self._python_package_dirs = set() |
|
27 |
|
28 def add_tree(self, source_dir, dest_dir): |
|
29 """Add a directory from where docs should be sourced.""" |
|
30 if dest_dir in self._trees: |
|
31 raise Exception('%s has already been registered as a destination.' |
|
32 % dest_dir) |
|
33 |
|
34 self._trees[dest_dir] = source_dir |
|
35 |
|
36 def add_python_package_dir(self, source_dir): |
|
37 """Add a directory containing Python packages. |
|
38 |
|
39 Added directories will have Python API docs generated automatically. |
|
40 """ |
|
41 self._python_package_dirs.add(source_dir) |
|
42 |
|
43 def generate_docs(self, fmt): |
|
44 """Generate documentation using Sphinx.""" |
|
45 self._synchronize_docs() |
|
46 self._generate_python_api_docs() |
|
47 |
|
48 old_env = os.environ.copy() |
|
49 try: |
|
50 os.environ['MOZILLA_DIR'] = self._topsrcdir |
|
51 args = [ |
|
52 'sphinx', |
|
53 '-b', fmt, |
|
54 os.path.join(self._output_dir, 'staging'), |
|
55 os.path.join(self._output_dir, fmt), |
|
56 ] |
|
57 |
|
58 return sphinx.main(args) |
|
59 finally: |
|
60 os.environ.clear() |
|
61 os.environ.update(old_env) |
|
62 |
|
63 def _generate_python_api_docs(self): |
|
64 """Generate Python API doc files.""" |
|
65 out_dir = os.path.join(self._output_dir, 'staging', 'python') |
|
66 base_args = ['sphinx', '--no-toc', '-o', out_dir] |
|
67 |
|
68 for p in sorted(self._python_package_dirs): |
|
69 full = os.path.join(self._topsrcdir, p) |
|
70 |
|
71 finder = FileFinder(full, find_executables=False) |
|
72 dirs = {os.path.dirname(f[0]) for f in finder.find('**')} |
|
73 |
|
74 excludes = {d for d in dirs if d.endswith('test')} |
|
75 |
|
76 args = list(base_args) |
|
77 args.append(full) |
|
78 args.extend(excludes) |
|
79 |
|
80 sphinx.apidoc.main(args) |
|
81 |
|
82 def _synchronize_docs(self): |
|
83 m = InstallManifest() |
|
84 |
|
85 m.add_symlink(self._conf_py_path, 'conf.py') |
|
86 |
|
87 for dest, source in sorted(self._trees.items()): |
|
88 source_dir = os.path.join(self._topsrcdir, source) |
|
89 for root, dirs, files in os.walk(source_dir): |
|
90 for f in files: |
|
91 source_path = os.path.join(root, f) |
|
92 rel_source = source_path[len(source_dir) + 1:] |
|
93 |
|
94 m.add_symlink(source_path, os.path.join(dest, rel_source)) |
|
95 |
|
96 stage_dir = os.path.join(self._output_dir, 'staging') |
|
97 copier = FileCopier() |
|
98 m.populate_registry(copier) |
|
99 copier.copy(stage_dir) |
|
100 |
|
101 with open(self._index_path, 'rb') as fh: |
|
102 data = fh.read() |
|
103 |
|
104 indexes = ['%s/index' % p for p in sorted(self._trees.keys())] |
|
105 indexes = '\n '.join(indexes) |
|
106 |
|
107 packages = [os.path.basename(p) for p in self._python_package_dirs] |
|
108 packages = ['python/%s' % p for p in packages] |
|
109 packages = '\n '.join(sorted(packages)) |
|
110 data = data.format(indexes=indexes, python_packages=packages) |
|
111 |
|
112 with open(os.path.join(stage_dir, 'index.rst'), 'wb') as fh: |
|
113 fh.write(data) |