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