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