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