third_party: Update waf to version 2.0.17
[bbaumbach/samba-autobuild/.git] / third_party / waf / waflib / extras / doxygen.py
1 #! /usr/bin/env python
2 # encoding: UTF-8
3 # Thomas Nagy 2008-2010 (ita)
4
5 """
6
7 Doxygen support
8
9 Variables passed to bld():
10 * doxyfile -- the Doxyfile to use
11 * doxy_tar -- destination archive for generated documentation (if desired)
12 * install_path -- where to install the documentation
13 * pars -- dictionary overriding doxygen configuration settings
14
15 When using this tool, the wscript will look like:
16
17         def options(opt):
18                 opt.load('doxygen')
19
20         def configure(conf):
21                 conf.load('doxygen')
22                 # check conf.env.DOXYGEN, if it is mandatory
23
24         def build(bld):
25                 if bld.env.DOXYGEN:
26                         bld(features="doxygen", doxyfile='Doxyfile', ...)
27 """
28
29 import os, os.path, re
30 from collections import OrderedDict
31 from waflib import Task, Utils, Node
32 from waflib.TaskGen import feature
33
34 DOXY_STR = '"${DOXYGEN}" - '
35 DOXY_FMTS = 'html latex man rft xml'.split()
36 DOXY_FILE_PATTERNS = '*.' + ' *.'.join('''
37 c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx hpp h++ idl odl cs php php3
38 inc m mm py f90c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx
39 '''.split())
40
41 re_rl = re.compile('\\\\\r*\n', re.MULTILINE)
42 re_nl = re.compile('\r*\n', re.M)
43 def parse_doxy(txt):
44         '''
45         Parses a doxygen file.
46         Returns an ordered dictionary. We cannot return a default dictionary, as the
47         order in which the entries are reported does matter, especially for the
48         '@INCLUDE' lines.
49         '''
50         tbl = OrderedDict()
51         txt   = re_rl.sub('', txt)
52         lines = re_nl.split(txt)
53         for x in lines:
54                 x = x.strip()
55                 if not x or x.startswith('#') or x.find('=') < 0:
56                         continue
57                 if x.find('+=') >= 0:
58                         tmp = x.split('+=')
59                         key = tmp[0].strip()
60                         if key in tbl:
61                                 tbl[key] += ' ' + '+='.join(tmp[1:]).strip()
62                         else:
63                                 tbl[key] = '+='.join(tmp[1:]).strip()
64                 else:
65                         tmp = x.split('=')
66                         tbl[tmp[0].strip()] = '='.join(tmp[1:]).strip()
67         return tbl
68
69 class doxygen(Task.Task):
70         vars  = ['DOXYGEN', 'DOXYFLAGS']
71         color = 'BLUE'
72
73         def runnable_status(self):
74                 '''
75                 self.pars are populated in runnable_status - because this function is being
76                 run *before* both self.pars "consumers" - scan() and run()
77
78                 set output_dir (node) for the output
79                 '''
80
81                 for x in self.run_after:
82                         if not x.hasrun:
83                                 return Task.ASK_LATER
84
85                 if not getattr(self, 'pars', None):
86                         txt = self.inputs[0].read()
87                         self.pars = parse_doxy(txt)
88                         if self.pars.get('OUTPUT_DIRECTORY'):
89                                 # Use the path parsed from the Doxyfile as an absolute path
90                                 output_node = self.inputs[0].parent.get_bld().make_node(self.pars['OUTPUT_DIRECTORY'])
91                         else:
92                                 # If no OUTPUT_PATH was specified in the Doxyfile, build path from the Doxyfile name + '.doxy'
93                                 output_node = self.inputs[0].parent.get_bld().make_node(self.inputs[0].name + '.doxy')
94                         output_node.mkdir()
95                         self.pars['OUTPUT_DIRECTORY'] = output_node.abspath()
96
97                         # Override with any parameters passed to the task generator
98                         if getattr(self.generator, 'pars', None):
99                                 for k, v in self.generator.pars.items():
100                                         self.pars[k] = v
101
102                         self.doxy_inputs = getattr(self, 'doxy_inputs', [])
103                         if not self.pars.get('INPUT'):
104                                 self.doxy_inputs.append(self.inputs[0].parent)
105                         else:
106                                 for i in self.pars.get('INPUT').split():
107                                         if os.path.isabs(i):
108                                                 node = self.generator.bld.root.find_node(i)
109                                         else:
110                                                 node = self.inputs[0].parent.find_node(i)
111                                         if not node:
112                                                 self.generator.bld.fatal('Could not find the doxygen input %r' % i)
113                                         self.doxy_inputs.append(node)
114
115                 if not getattr(self, 'output_dir', None):
116                         bld = self.generator.bld
117                         # Output path is always an absolute path as it was transformed above.
118                         self.output_dir = bld.root.find_dir(self.pars['OUTPUT_DIRECTORY'])
119
120                 self.signature()
121                 ret = Task.Task.runnable_status(self)
122                 if ret == Task.SKIP_ME:
123                         # in case the files were removed
124                         self.add_install()
125                 return ret
126
127         def scan(self):
128                 exclude_patterns = self.pars.get('EXCLUDE_PATTERNS','').split()
129                 exclude_patterns = [pattern.replace('*/', '**/') for pattern in exclude_patterns]
130                 file_patterns = self.pars.get('FILE_PATTERNS','').split()
131                 if not file_patterns:
132                         file_patterns = DOXY_FILE_PATTERNS.split()
133                 if self.pars.get('RECURSIVE') == 'YES':
134                         file_patterns = ["**/%s" % pattern for pattern in file_patterns]
135                 nodes = []
136                 names = []
137                 for node in self.doxy_inputs:
138                         if os.path.isdir(node.abspath()):
139                                 for m in node.ant_glob(incl=file_patterns, excl=exclude_patterns):
140                                         nodes.append(m)
141                         else:
142                                 nodes.append(node)
143                 return (nodes, names)
144
145         def run(self):
146                 dct = self.pars.copy()
147                 code = '\n'.join(['%s = %s' % (x, dct[x]) for x in self.pars])
148                 code = code.encode() # for python 3
149                 #fmt = DOXY_STR % (self.inputs[0].parent.abspath())
150                 cmd = Utils.subst_vars(DOXY_STR, self.env)
151                 env = self.env.env or None
152                 proc = Utils.subprocess.Popen(cmd, shell=True, stdin=Utils.subprocess.PIPE, env=env, cwd=self.inputs[0].parent.abspath())
153                 proc.communicate(code)
154                 return proc.returncode
155
156         def post_run(self):
157                 nodes = self.output_dir.ant_glob('**/*', quiet=True)
158                 for x in nodes:
159                         self.generator.bld.node_sigs[x] = self.uid()
160                 self.add_install()
161                 return Task.Task.post_run(self)
162
163         def add_install(self):
164                 nodes = self.output_dir.ant_glob('**/*', quiet=True)
165                 self.outputs += nodes
166                 if getattr(self.generator, 'install_path', None):
167                         if not getattr(self.generator, 'doxy_tar', None):
168                                 self.generator.add_install_files(install_to=self.generator.install_path,
169                                         install_from=self.outputs,
170                                         postpone=False,
171                                         cwd=self.output_dir,
172                                         relative_trick=True)
173
174 class tar(Task.Task):
175         "quick tar creation"
176         run_str = '${TAR} ${TAROPTS} ${TGT} ${SRC}'
177         color   = 'RED'
178         after   = ['doxygen']
179         def runnable_status(self):
180                 for x in getattr(self, 'input_tasks', []):
181                         if not x.hasrun:
182                                 return Task.ASK_LATER
183
184                 if not getattr(self, 'tar_done_adding', None):
185                         # execute this only once
186                         self.tar_done_adding = True
187                         for x in getattr(self, 'input_tasks', []):
188                                 self.set_inputs(x.outputs)
189                         if not self.inputs:
190                                 return Task.SKIP_ME
191                 return Task.Task.runnable_status(self)
192
193         def __str__(self):
194                 tgt_str = ' '.join([a.path_from(a.ctx.launch_node()) for a in self.outputs])
195                 return '%s: %s\n' % (self.__class__.__name__, tgt_str)
196
197 @feature('doxygen')
198 def process_doxy(self):
199         if not getattr(self, 'doxyfile', None):
200                 self.bld.fatal('no doxyfile variable specified??')
201
202         node = self.doxyfile
203         if not isinstance(node, Node.Node):
204                 node = self.path.find_resource(node)
205         if not node:
206                 self.bld.fatal('doxygen file %s not found' % self.doxyfile)
207
208         # the task instance
209         dsk = self.create_task('doxygen', node)
210
211         if getattr(self, 'doxy_tar', None):
212                 tsk = self.create_task('tar')
213                 tsk.input_tasks = [dsk]
214                 tsk.set_outputs(self.path.find_or_declare(self.doxy_tar))
215                 if self.doxy_tar.endswith('bz2'):
216                         tsk.env['TAROPTS'] = ['cjf']
217                 elif self.doxy_tar.endswith('gz'):
218                         tsk.env['TAROPTS'] = ['czf']
219                 else:
220                         tsk.env['TAROPTS'] = ['cf']
221                 if getattr(self, 'install_path', None):
222                         self.add_install_files(install_to=self.install_path, install_from=tsk.outputs)
223
224 def configure(conf):
225         '''
226         Check if doxygen and tar commands are present in the system
227
228         If the commands are present, then conf.env.DOXYGEN and conf.env.TAR
229         variables will be set. Detection can be controlled by setting DOXYGEN and
230         TAR environmental variables.
231         '''
232
233         conf.find_program('doxygen', var='DOXYGEN', mandatory=False)
234         conf.find_program('tar', var='TAR', mandatory=False)