Alexander Afanasyev | a1ae0a1 | 2014-01-28 15:21:02 -0800 | [diff] [blame^] | 1 | #!/usr/bin/env python |
| 2 | # encoding: utf-8 |
| 3 | # Hans-Martin von Gaudecker, 2012 |
| 4 | |
| 5 | """ |
| 6 | Create Sphinx documentation. Currently only LaTeX and HTML are supported. |
| 7 | |
| 8 | The source file **must** be the conf.py file used by Sphinx. Everything |
| 9 | else has defaults, passing in the parameters is optional. |
| 10 | |
| 11 | Usage for getting both html and pdf docs: |
| 12 | |
| 13 | ctx(features='sphinx', source='docs/conf.py') |
| 14 | ctx(features='sphinx', source='docs/conf.py', buildername='latex') |
| 15 | |
| 16 | Optional parameters and their defaults: |
| 17 | |
| 18 | * buildername: html |
| 19 | * srcdir: confdir (the directory where conf.py lives) |
| 20 | * outdir: confdir/buildername (in the build directory tree) |
| 21 | * doctreedir: outdir/.doctrees |
| 22 | * type: pdflatex (only applies to 'latex' builder) |
| 23 | |
| 24 | """ |
| 25 | |
| 26 | |
| 27 | import os |
| 28 | from waflib import Task, TaskGen, Errors, Logs |
| 29 | |
| 30 | class RunSphinxBuild(Task.Task): |
| 31 | def scan(self): |
| 32 | """Use Sphinx' internal environment to find the dependencies.""" |
| 33 | s = self.sphinx_instance |
| 34 | msg, dummy, iterator = s.env.update(s.config, s.srcdir, s.doctreedir, s) |
| 35 | s.info(msg) |
| 36 | dep_nodes = [] |
| 37 | for docname in s.builder.status_iterator(iterator, "reading sources... "): |
| 38 | filename = docname + s.config.source_suffix |
| 39 | dep_nodes.append(self.srcdir.find_node(filename)) |
| 40 | for dep in s.env.dependencies.values(): |
| 41 | # Need the 'str' call because Sphinx might return Unicode strings. |
| 42 | [dep_nodes.append(self.srcdir.find_node(str(d))) for d in dep] |
| 43 | return (dep_nodes, []) |
| 44 | |
| 45 | def run(self): |
| 46 | """Run the Sphinx build.""" |
| 47 | self.sphinx_instance.build(force_all=False, filenames=None) |
| 48 | return None |
| 49 | |
| 50 | def post_run(self): |
| 51 | """Add everything found in the output directory tree as an output. |
| 52 | Not elegant, but pragmatic.""" |
| 53 | for n in self.outdir.ant_glob("**", quiet=True, remove=False): |
| 54 | if n not in self.outputs: self.set_outputs(n) |
| 55 | super(RunSphinxBuild, self).post_run() |
| 56 | |
| 57 | |
| 58 | def _get_main_targets(tg, s): |
| 59 | """Return some easy targets known from the Sphinx build environment **s.env**.""" |
| 60 | out_dir = tg.bld.root.find_node(s.outdir) |
| 61 | tgt_nodes = [] |
| 62 | if s.builder.name == "latex": |
| 63 | for tgt_info in s.env.config.latex_documents: |
| 64 | tgt_nodes.append(out_dir.find_or_declare(tgt_info[1])) |
| 65 | elif s.builder.name == "html": |
| 66 | suffix = getattr(s.env.config, "html_file_suffix", ".html") |
| 67 | tgt_name = s.env.config.master_doc + suffix |
| 68 | tgt_nodes.append(out_dir.find_or_declare(tgt_name)) |
| 69 | else: |
| 70 | raise Errors.WafError("Sphinx builder not implemented: %s" % s.builder.name) |
| 71 | return tgt_nodes |
| 72 | |
| 73 | |
| 74 | @TaskGen.feature("sphinx") |
| 75 | @TaskGen.before_method("process_source") |
| 76 | def apply_sphinx(tg): |
| 77 | """Set up the task generator with a Sphinx instance and create a task.""" |
| 78 | |
| 79 | from sphinx.application import Sphinx |
| 80 | |
| 81 | # Put together the configuration based on defaults and tg attributes. |
| 82 | conf = tg.path.find_node(tg.source) |
| 83 | confdir = conf.parent.abspath() |
| 84 | buildername = getattr(tg, "buildername", "html") |
| 85 | srcdir = getattr(tg, "srcdir", confdir) |
| 86 | outdir = tg.path.find_or_declare (getattr(tg, "outdir", os.path.join(conf.parent.get_bld().abspath(), buildername))).abspath () |
| 87 | |
| 88 | doctreedir = getattr(tg, "doctreedir", os.path.join(outdir, ".doctrees")) |
| 89 | |
| 90 | # Set up the Sphinx instance. |
| 91 | s = Sphinx (srcdir, confdir, outdir, doctreedir, buildername, status=None) |
| 92 | |
| 93 | # Get the main targets of the Sphinx build. |
| 94 | tgt_nodes = _get_main_targets(tg, s) |
| 95 | |
| 96 | # Create the task and set the required attributes. |
| 97 | task = tg.create_task("RunSphinxBuild", src=conf, tgt=tgt_nodes) |
| 98 | task.srcdir = tg.bld.root.find_node(s.srcdir) |
| 99 | task.outdir = tg.bld.root.find_node(s.outdir) |
| 100 | task.sphinx_instance = s |
| 101 | |
| 102 | # Build pdf if we have the LaTeX builder, allow for building with xelatex. |
| 103 | if s.builder.name == "latex": |
| 104 | compile_type = getattr(tg, "type", "pdflatex") |
| 105 | tg.bld(features="tex", type=compile_type, source=tgt_nodes, name="sphinx_pdf", prompt=0) |
| 106 | |
| 107 | # Bypass the execution of process_source by setting the source to an empty list |
| 108 | tg.source = [] |