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