1 # Copyright 2019 The Pigweed Authors
3 # Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 # use this file except in compliance with the License. You may obtain a copy of
7 # https://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 # License for the specific language governing permissions and limitations under
14 """Renders HTML documentation using Sphinx."""
16 # TODO(frolv): Figure out a solution for installing all library dependencies
17 # to run Sphinx and build RTD docs.
27 from typing import Dict, List, Tuple
29 SCRIPT_HEADER: str = '''
30 ██████╗ ██╗ ██████╗ ██╗ ██╗███████╗███████╗██████╗ ██████╗ ██████╗ ██████╗███████╗
31 ██╔══██╗██║██╔════╝ ██║ ██║██╔════╝██╔════╝██╔══██╗ ██╔══██╗██╔═══██╗██╔════╝██╔════╝
32 ██████╔╝██║██║ ███╗██║ █╗ ██║█████╗ █████╗ ██║ ██║ ██║ ██║██║ ██║██║ ███████╗
33 ██╔═══╝ ██║██║ ██║██║███╗██║██╔══╝ ██╔══╝ ██║ ██║ ██║ ██║██║ ██║██║ ╚════██║
34 ██║ ██║╚██████╔╝╚███╔███╔╝███████╗███████╗██████╔╝ ██████╔╝╚██████╔╝╚██████╗███████║
35 ╚═╝ ╚═╝ ╚═════╝ ╚══╝╚══╝ ╚══════╝╚══════╝╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝╚══════╝
39 def parse_args() -> argparse.Namespace:
40 """Parses command-line arguments."""
42 parser = argparse.ArgumentParser(description=__doc__)
43 parser.add_argument('--sphinx-build-dir',
45 help='Directory in which to build docs')
46 parser.add_argument('--conf',
48 help='Path to conf.py file for Sphinx')
49 parser.add_argument('--gn-root',
51 help='Root of the GN build tree')
52 parser.add_argument('--gn-gen-root',
54 help='Root of the GN gen tree')
55 parser.add_argument('sources',
57 help='Paths to the root level rst source files')
58 parser.add_argument('--out-dir',
60 help='Output directory for rendered HTML docs')
61 parser.add_argument('--metadata',
63 type=argparse.FileType('r'),
64 help='Metadata JSON file')
65 return parser.parse_args()
68 def build_docs(src_dir: str, dst_dir: str) -> int:
69 """Runs Sphinx to render HTML documentation from a doc tree."""
71 # TODO(frolv): Specify the Sphinx script from a prebuilts path instead of
72 # requiring it in the tree.
74 'sphinx-build', '-W', '-b', 'html', '-d', f'{dst_dir}/help', src_dir,
77 return subprocess.call(command)
80 def mkdir(dirname: str, exist_ok: bool = False) -> None:
81 """Wrapper around os.makedirs that prints the operation."""
82 print(f'MKDIR {dirname}')
83 os.makedirs(dirname, exist_ok=exist_ok)
86 def copy(src: str, dst: str) -> None:
87 """Wrapper around shutil.copy that prints the operation."""
88 print(f'COPY {src} -> {dst}')
92 def copy_doc_tree(args: argparse.Namespace) -> None:
93 """Copies doc source and input files into a build tree."""
95 """Converts a source path to a filename in the build directory."""
96 if path.startswith(args.gn_root):
97 path = os.path.relpath(path, args.gn_root)
98 elif path.startswith(args.gn_gen_root):
99 path = os.path.relpath(path, args.gn_gen_root)
101 return os.path.join(args.sphinx_build_dir, path)
103 source_files = json.load(args.metadata)
104 copy_paths = [build_path(f) for f in source_files]
106 mkdir(args.sphinx_build_dir)
107 for source_path in args.sources:
108 copy(source_path, f'{args.sphinx_build_dir}/')
109 copy(args.conf, f'{args.sphinx_build_dir}/conf.py')
111 # Map of directory path to list of source and destination file paths.
112 dirs: Dict[str, List[Tuple[str, str]]] = collections.defaultdict(list)
114 for source_file, copy_path in zip(source_files, copy_paths):
115 dirname = os.path.dirname(copy_path)
116 dirs[dirname].append((source_file, copy_path))
118 for directory, file_pairs in dirs.items():
119 mkdir(directory, exist_ok=True)
120 for src, dst in file_pairs:
125 """Script entry point."""
129 # Clear out any existing docs for the target.
130 if os.path.exists(args.sphinx_build_dir):
131 shutil.rmtree(args.sphinx_build_dir)
133 # TODO(pwbug/164): Printing the header causes unicode problems on Windows.
134 # Disabled for now; re-enable once the root issue is fixed.
135 # print(SCRIPT_HEADER)
138 # Flush all script output before running Sphinx.
139 print('-' * 80, flush=True)
141 return build_docs(args.sphinx_build_dir, args.out_dir)
144 if __name__ == '__main__':