thirdparty:waf: New files for waf 1.9.10
[Samba.git] / third_party / waf / waflib / extras / rst.py
blobc8cd7526dccca38eb12d6729ce9ad1c44bcac81d
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # Jérôme Carretero, 2013 (zougloub)
5 """
6 reStructuredText support (experimental)
8 Example::
10 def configure(conf):
11 conf.load('rst')
12 if not conf.env.RST2HTML:
13 conf.fatal('The program rst2html is required')
15 def build(bld):
16 bld(
17 features = 'rst',
18 type = 'rst2html', # rst2html, rst2pdf, ...
19 source = 'index.rst', # mandatory, the source
20 deps = 'image.png', # to give additional non-trivial dependencies
23 By default the tool looks for a set of programs in PATH.
24 The tools are defined in `rst_progs`.
25 To configure with a special program use::
27 $ RST2HTML=/path/to/rst2html waf configure
29 This tool is experimental; don't hesitate to contribute to it.
31 """
33 import re
34 from waflib import Node, Utils, Task, Errors, Logs
35 from waflib.TaskGen import feature, before_method
37 rst_progs = "rst2html rst2xetex rst2latex rst2xml rst2pdf rst2s5 rst2man rst2odt rst2rtf".split()
39 def parse_rst_node(node, nodes, names, seen):
40 # TODO add extensibility, to handle custom rst include tags...
41 if node in seen:
42 return
43 seen.append(node)
44 code = node.read()
45 re_rst = re.compile(r'^\s*.. ((?P<subst>\|\S+\|) )?(?P<type>include|image|figure):: (?P<file>.*)$', re.M)
46 for match in re_rst.finditer(code):
47 ipath = match.group('file')
48 itype = match.group('type')
49 Logs.debug("rst: visiting %s: %s" % (itype, ipath))
50 found = node.parent.find_resource(ipath)
51 if found:
52 nodes.append(found)
53 if itype == 'include':
54 parse_rst_node(found, nodes, names, seen)
55 else:
56 names.append(ipath)
58 class docutils(Task.Task):
59 """
60 Compile a rst file.
61 """
63 def scan(self):
64 """
65 A recursive regex-based scanner that finds rst dependencies.
66 """
68 nodes = []
69 names = []
70 seen = []
72 node = self.inputs[0]
74 if not node:
75 return (nodes, names)
77 parse_rst_node(node, nodes, names, seen)
79 Logs.debug("rst: %s: found the following file deps: %s" % (repr(self), nodes))
80 if names:
81 Logs.warn("rst: %s: could not find the following file deps: %s" % (repr(self), names))
83 return (nodes, names)
85 def check_status(self, msg, retcode):
86 """
87 Check an exit status and raise an error with a particular message
89 :param msg: message to display if the code is non-zero
90 :type msg: string
91 :param retcode: condition
92 :type retcode: boolean
93 """
94 if retcode != 0:
95 raise Errors.WafError("%r command exit status %r" % (msg, retcode))
97 def run(self):
98 """
99 Runs the rst compilation using docutils
101 raise NotImplementedError()
103 class rst2html(docutils):
104 color = 'BLUE'
106 def __init__(self, *args, **kw):
107 docutils.__init__(self, *args, **kw)
108 self.command = self.generator.env.RST2HTML
109 self.attributes = ['stylesheet']
111 def scan(self):
112 nodes, names = docutils.scan(self)
114 for attribute in self.attributes:
115 stylesheet = getattr(self.generator, attribute, None)
116 if stylesheet is not None:
117 ssnode = self.generator.to_nodes(stylesheet)[0]
118 nodes.append(ssnode)
119 Logs.debug("rst: adding dep to %s %s" % (attribute, stylesheet))
121 return nodes, names
123 def run(self):
124 cwdn = self.outputs[0].parent
125 src = self.inputs[0].path_from(cwdn)
126 dst = self.outputs[0].path_from(cwdn)
128 cmd = self.command + [src, dst]
129 cmd += Utils.to_list(getattr(self.generator, 'options', []))
130 for attribute in self.attributes:
131 stylesheet = getattr(self.generator, attribute, None)
132 if stylesheet is not None:
133 stylesheet = self.generator.to_nodes(stylesheet)[0]
134 cmd += ['--%s' % attribute, stylesheet.path_from(cwdn)]
136 return self.exec_command(cmd, cwd=cwdn.abspath())
138 class rst2s5(rst2html):
139 def __init__(self, *args, **kw):
140 rst2html.__init__(self, *args, **kw)
141 self.command = self.generator.env.RST2S5
142 self.attributes = ['stylesheet']
144 class rst2latex(rst2html):
145 def __init__(self, *args, **kw):
146 rst2html.__init__(self, *args, **kw)
147 self.command = self.generator.env.RST2LATEX
148 self.attributes = ['stylesheet']
150 class rst2xetex(rst2html):
151 def __init__(self, *args, **kw):
152 rst2html.__init__(self, *args, **kw)
153 self.command = self.generator.env.RST2XETEX
154 self.attributes = ['stylesheet']
156 class rst2pdf(docutils):
157 color = 'BLUE'
158 def run(self):
159 cwdn = self.outputs[0].parent
160 src = self.inputs[0].path_from(cwdn)
161 dst = self.outputs[0].path_from(cwdn)
163 cmd = self.generator.env.RST2PDF + [src, '-o', dst]
164 cmd += Utils.to_list(getattr(self.generator, 'options', []))
166 return self.exec_command(cmd, cwd=cwdn.abspath())
169 @feature('rst')
170 @before_method('process_source')
171 def apply_rst(self):
173 Create :py:class:`rst` or other rst-related task objects
176 if self.target:
177 if isinstance(self.target, Node.Node):
178 tgt = self.target
179 elif isinstance(self.target, str):
180 tgt = self.path.get_bld().make_node(self.target)
181 else:
182 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))
183 else:
184 tgt = None
186 tsk_type = getattr(self, 'type', None)
188 src = self.to_nodes(self.source)
189 assert len(src) == 1
190 src = src[0]
192 if tsk_type is not None and tgt is None:
193 if tsk_type.startswith('rst2'):
194 ext = tsk_type[4:]
195 else:
196 self.bld.fatal("rst: Could not detect the output file extension for %s" % self)
197 tgt = src.change_ext('.%s' % ext)
198 elif tsk_type is None and tgt is not None:
199 out = tgt.name
200 ext = out[out.rfind('.')+1:]
201 self.type = 'rst2' + ext
202 elif tsk_type is not None and tgt is not None:
203 # the user knows what he wants
204 pass
205 else:
206 self.bld.fatal("rst: Need to indicate task type or target name for %s" % self)
208 deps_lst = []
210 if getattr(self, 'deps', None):
211 deps = self.to_list(self.deps)
212 for filename in deps:
213 n = self.path.find_resource(filename)
214 if not n:
215 self.bld.fatal('Could not find %r for %r' % (filename, self))
216 if not n in deps_lst:
217 deps_lst.append(n)
219 try:
220 task = self.create_task(self.type, src, tgt)
221 except KeyError:
222 self.bld.fatal("rst: Task of type %s not implemented (created by %s)" % (self.type, self))
224 task.env = self.env
226 # add the manual dependencies
227 if deps_lst:
228 try:
229 lst = self.bld.node_deps[task.uid()]
230 for n in deps_lst:
231 if not n in lst:
232 lst.append(n)
233 except KeyError:
234 self.bld.node_deps[task.uid()] = deps_lst
236 inst_to = getattr(self, 'install_path', None)
237 if inst_to:
238 self.install_task = self.bld.install_files(inst_to, task.outputs[:], env=self.env)
240 self.source = []
242 def configure(self):
244 Try to find the rst programs.
246 Do not raise any error if they are not found.
247 You'll have to use additional code in configure() to die
248 if programs were not found.
250 for p in rst_progs:
251 self.find_program(p, mandatory=False)