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