third_party:waf: update to upstream 2.0.4 release
[bbaumbach/samba-autobuild/.git] / third_party / waf / waflib / extras / rst.py
1 #! /usr/bin/env python
2 # encoding: utf-8
3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
4
5 #!/usr/bin/env python
6 # encoding: utf-8
7 # Jérôme Carretero, 2013 (zougloub)
8
9 """
10 reStructuredText support (experimental)
11
12 Example::
13
14         def configure(conf):
15                 conf.load('rst')
16                 if not conf.env.RST2HTML:
17                         conf.fatal('The program rst2html is required')
18
19         def build(bld):
20                 bld(
21                  features = 'rst',
22                  type     = 'rst2html', # rst2html, rst2pdf, ...
23                  source   = 'index.rst', # mandatory, the source
24                  deps     = 'image.png', # to give additional non-trivial dependencies
25                 )
26
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::
30
31         $ RST2HTML=/path/to/rst2html waf configure
32
33 This tool is experimental; don't hesitate to contribute to it.
34
35 """
36
37 import re
38 from waflib import Node, Utils, Task, Errors, Logs
39 from waflib.TaskGen import feature, before_method
40
41 rst_progs = "rst2html rst2xetex rst2latex rst2xml rst2pdf rst2s5 rst2man rst2odt rst2rtf".split()
42
43 def parse_rst_node(task, node, nodes, names, seen, dirs=None):
44         # TODO add extensibility, to handle custom rst include tags...
45         if dirs is None:
46                 dirs = (node.parent,node.get_bld().parent)
47
48         if node in seen:
49                 return
50         seen.append(node)
51         code = node.read()
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)
57                 found = False
58                 for d in dirs:
59                         Logs.debug('rst: looking for %s in %s', ipath, d.abspath())
60                         found = d.find_node(ipath)
61                         if found:
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)
66                                 break
67                 if not found:
68                         names.append((itype, ipath))
69
70 class docutils(Task.Task):
71         """
72         Compile a rst file.
73         """
74
75         def scan(self):
76                 """
77                 A recursive regex-based scanner that finds rst dependencies.
78                 """
79
80                 nodes = []
81                 names = []
82                 seen = []
83
84                 node = self.inputs[0]
85
86                 if not node:
87                         return (nodes, names)
88
89                 parse_rst_node(self, node, nodes, names, seen)
90
91                 Logs.debug('rst: %r: found the following file deps: %r', self, nodes)
92                 if names:
93                         Logs.warn('rst: %r: could not find the following file deps: %r', self, names)
94
95                 return ([v for (t,v) in nodes], [v for (t,v) in names])
96
97         def check_status(self, msg, retcode):
98                 """
99                 Check an exit status and raise an error with a particular message
100
101                 :param msg: message to display if the code is non-zero
102                 :type msg: string
103                 :param retcode: condition
104                 :type retcode: boolean
105                 """
106                 if retcode != 0:
107                         raise Errors.WafError('%r command exit status %r' % (msg, retcode))
108
109         def run(self):
110                 """
111                 Runs the rst compilation using docutils
112                 """
113                 raise NotImplementedError()
114
115 class rst2html(docutils):
116         color = 'BLUE'
117
118         def __init__(self, *args, **kw):
119                 docutils.__init__(self, *args, **kw)
120                 self.command = self.generator.env.RST2HTML
121                 self.attributes = ['stylesheet']
122
123         def scan(self):
124                 nodes, names = docutils.scan(self)
125
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]
130                                 nodes.append(ssnode)
131                                 Logs.debug('rst: adding dep to %s %s', attribute, stylesheet)
132
133                 return nodes, names
134
135         def run(self):
136                 cwdn = self.outputs[0].parent
137                 src = self.inputs[0].path_from(cwdn)
138                 dst = self.outputs[0].path_from(cwdn)
139
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)]
147
148                 return self.exec_command(cmd, cwd=cwdn.abspath())
149
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']
155
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']
161
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']
167
168 class rst2pdf(docutils):
169         color = 'BLUE'
170         def run(self):
171                 cwdn = self.outputs[0].parent
172                 src = self.inputs[0].path_from(cwdn)
173                 dst = self.outputs[0].path_from(cwdn)
174
175                 cmd = self.generator.env.RST2PDF + [src, '-o', dst]
176                 cmd += Utils.to_list(getattr(self.generator, 'options', []))
177
178                 return self.exec_command(cmd, cwd=cwdn.abspath())
179
180
181 @feature('rst')
182 @before_method('process_source')
183 def apply_rst(self):
184         """
185         Create :py:class:`rst` or other rst-related task objects
186         """
187
188         if self.target:
189                 if isinstance(self.target, Node.Node):
190                         tgt = self.target
191                 elif isinstance(self.target, str):
192                         tgt = self.path.get_bld().make_node(self.target)
193                 else:
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))
195         else:
196                 tgt = None
197
198         tsk_type = getattr(self, 'type', None)
199
200         src = self.to_nodes(self.source)
201         assert len(src) == 1
202         src = src[0]
203
204         if tsk_type is not None and tgt is None:
205                 if tsk_type.startswith('rst2'):
206                         ext = tsk_type[4:]
207                 else:
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:
211                 out = tgt.name
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
216                 pass
217         else:
218                 self.bld.fatal("rst: Need to indicate task type or target name for %s" % self)
219
220         deps_lst = []
221
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)
226                         if not n:
227                                 self.bld.fatal('Could not find %r for %r' % (filename, self))
228                         if not n in deps_lst:
229                                 deps_lst.append(n)
230
231         try:
232                 task = self.create_task(self.type, src, tgt)
233         except KeyError:
234                 self.bld.fatal("rst: Task of type %s not implemented (created by %s)" % (self.type, self))
235
236         task.env = self.env
237
238         # add the manual dependencies
239         if deps_lst:
240                 try:
241                         lst = self.bld.node_deps[task.uid()]
242                         for n in deps_lst:
243                                 if not n in lst:
244                                         lst.append(n)
245                 except KeyError:
246                         self.bld.node_deps[task.uid()] = deps_lst
247
248         inst_to = getattr(self, 'install_path', None)
249         if inst_to:
250                 self.install_task = self.add_install_files(install_to=inst_to, install_from=task.outputs[:])
251
252         self.source = []
253
254 def configure(self):
255         """
256         Try to find the rst programs.
257
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.
261         """
262         for p in rst_progs:
263                 self.find_program(p, mandatory=False)
264