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