3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
7 # Jérôme Carretero, 2013 (zougloub)
10 reStructuredText support (experimental)
16 if not conf.env.RST2HTML:
17 conf.fatal('The program rst2html is required')
22 type = 'rst2html', # rst2html, rst2pdf, ...
23 source = 'index.rst', # mandatory, the source
24 deps = 'image.png', # to give additional non-trivial dependencies
27 By default the tool looks for a set of programs in PATH.
28 The tools are defined in `rst_progs`.
29 To configure with a special program use::
31 $ RST2HTML=/path/to/rst2html waf configure
33 This tool is experimental; don't hesitate to contribute to it.
38 from waflib import Node, Utils, Task, Errors, Logs
39 from waflib.TaskGen import feature, before_method
41 rst_progs = "rst2html rst2xetex rst2latex rst2xml rst2pdf rst2s5 rst2man rst2odt rst2rtf".split()
43 def parse_rst_node(task, node, nodes, names, seen, dirs=None):
44 # TODO add extensibility, to handle custom rst include tags...
46 dirs = (node.parent,node.get_bld().parent)
52 re_rst = re.compile(r'^\s*.. ((?P<subst>\|\S+\|) )?(?P<type>include|image|figure):: (?P<file>.*)$', re.M)
53 for match in re_rst.finditer(code):
54 ipath = match.group('file')
55 itype = match.group('type')
56 Logs.debug('rst: visiting %s: %s', itype, ipath)
59 Logs.debug('rst: looking for %s in %s', ipath, d.abspath())
60 found = d.find_node(ipath)
62 Logs.debug('rst: found %s as %s', ipath, found.abspath())
63 nodes.append((itype, found))
64 if itype == 'include':
65 parse_rst_node(task, found, nodes, names, seen)
68 names.append((itype, ipath))
70 class docutils(Task.Task):
77 A recursive regex-based scanner that finds rst dependencies.
89 parse_rst_node(self, node, nodes, names, seen)
91 Logs.debug('rst: %r: found the following file deps: %r', self, nodes)
93 Logs.warn('rst: %r: could not find the following file deps: %r', self, names)
95 return ([v for (t,v) in nodes], [v for (t,v) in names])
97 def check_status(self, msg, retcode):
99 Check an exit status and raise an error with a particular message
101 :param msg: message to display if the code is non-zero
103 :param retcode: condition
104 :type retcode: boolean
107 raise Errors.WafError('%r command exit status %r' % (msg, retcode))
111 Runs the rst compilation using docutils
113 raise NotImplementedError()
115 class rst2html(docutils):
118 def __init__(self, *args, **kw):
119 docutils.__init__(self, *args, **kw)
120 self.command = self.generator.env.RST2HTML
121 self.attributes = ['stylesheet']
124 nodes, names = docutils.scan(self)
126 for attribute in self.attributes:
127 stylesheet = getattr(self.generator, attribute, None)
128 if stylesheet is not None:
129 ssnode = self.generator.to_nodes(stylesheet)[0]
131 Logs.debug('rst: adding dep to %s %s', attribute, stylesheet)
136 cwdn = self.outputs[0].parent
137 src = self.inputs[0].path_from(cwdn)
138 dst = self.outputs[0].path_from(cwdn)
140 cmd = self.command + [src, dst]
141 cmd += Utils.to_list(getattr(self.generator, 'options', []))
142 for attribute in self.attributes:
143 stylesheet = getattr(self.generator, attribute, None)
144 if stylesheet is not None:
145 stylesheet = self.generator.to_nodes(stylesheet)[0]
146 cmd += ['--%s' % attribute, stylesheet.path_from(cwdn)]
148 return self.exec_command(cmd, cwd=cwdn.abspath())
150 class rst2s5(rst2html):
151 def __init__(self, *args, **kw):
152 rst2html.__init__(self, *args, **kw)
153 self.command = self.generator.env.RST2S5
154 self.attributes = ['stylesheet']
156 class rst2latex(rst2html):
157 def __init__(self, *args, **kw):
158 rst2html.__init__(self, *args, **kw)
159 self.command = self.generator.env.RST2LATEX
160 self.attributes = ['stylesheet']
162 class rst2xetex(rst2html):
163 def __init__(self, *args, **kw):
164 rst2html.__init__(self, *args, **kw)
165 self.command = self.generator.env.RST2XETEX
166 self.attributes = ['stylesheet']
168 class rst2pdf(docutils):
171 cwdn = self.outputs[0].parent
172 src = self.inputs[0].path_from(cwdn)
173 dst = self.outputs[0].path_from(cwdn)
175 cmd = self.generator.env.RST2PDF + [src, '-o', dst]
176 cmd += Utils.to_list(getattr(self.generator, 'options', []))
178 return self.exec_command(cmd, cwd=cwdn.abspath())
182 @before_method('process_source')
185 Create :py:class:`rst` or other rst-related task objects
189 if isinstance(self.target, Node.Node):
191 elif isinstance(self.target, str):
192 tgt = self.path.get_bld().make_node(self.target)
194 self.bld.fatal("rst: Don't know how to build target name %s which is not a string or Node for %s" % (self.target, self))
198 tsk_type = getattr(self, 'type', None)
200 src = self.to_nodes(self.source)
204 if tsk_type is not None and tgt is None:
205 if tsk_type.startswith('rst2'):
208 self.bld.fatal("rst: Could not detect the output file extension for %s" % self)
209 tgt = src.change_ext('.%s' % ext)
210 elif tsk_type is None and tgt is not None:
212 ext = out[out.rfind('.')+1:]
213 self.type = 'rst2' + ext
214 elif tsk_type is not None and tgt is not None:
215 # the user knows what he wants
218 self.bld.fatal("rst: Need to indicate task type or target name for %s" % self)
222 if getattr(self, 'deps', None):
223 deps = self.to_list(self.deps)
224 for filename in deps:
225 n = self.path.find_resource(filename)
227 self.bld.fatal('Could not find %r for %r' % (filename, self))
228 if not n in deps_lst:
232 task = self.create_task(self.type, src, tgt)
234 self.bld.fatal("rst: Task of type %s not implemented (created by %s)" % (self.type, self))
238 # add the manual dependencies
241 lst = self.bld.node_deps[task.uid()]
246 self.bld.node_deps[task.uid()] = deps_lst
248 inst_to = getattr(self, 'install_path', None)
250 self.install_task = self.add_install_files(install_to=inst_to, install_from=task.outputs[:])
256 Try to find the rst programs.
258 Do not raise any error if they are not found.
259 You'll have to use additional code in configure() to die
260 if programs were not found.
263 self.find_program(p, mandatory=False)