blob: 18b32929427a0e3193b596042c04c39a0aaa5ce3 [file] [log] [blame]
Alexander Afanasyev35fc2b72014-02-13 16:56:21 -08001# Thomas Nagy 2008-2010 (ita)
2
3"""
Alexander Afanasyev35fc2b72014-02-13 16:56:21 -08004Doxygen support
5
6Variables passed to bld():
7* doxyfile -- the Doxyfile to use
8
9When 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', ...)
Alexander Afanasyev97e4cac2014-03-28 10:55:11 -070021
22 def doxygen(bld):
23 if bld.env.DOXYGEN:
24 bld(features="doxygen", doxyfile='Doxyfile', ...)
Alexander Afanasyev35fc2b72014-02-13 16:56:21 -080025"""
26
27from fnmatch import fnmatchcase
28import os, os.path, re, stat
Alexander Afanasyev97e4cac2014-03-28 10:55:11 -070029from waflib import Task, Utils, Node, Logs, Errors, Build
Alexander Afanasyev35fc2b72014-02-13 16:56:21 -080030from waflib.TaskGen import feature
31
32DOXY_STR = '"${DOXYGEN}" - '
33DOXY_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
83 # Override with any parameters passed to the task generator
84 if getattr(self.generator, 'pars', None):
Alexander Afanasyevdd7e5da2016-03-02 15:33:20 -080085 for k, v in self.generator.pars.items():
Alexander Afanasyev35fc2b72014-02-13 16:56:21 -080086 self.pars[k] = v
87
88 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):
112 exclude_patterns = self.pars.get('EXCLUDE_PATTERNS','').split()
113 file_patterns = self.pars.get('FILE_PATTERNS','').split()
114 if not file_patterns:
115 file_patterns = DOXY_FILE_PATTERNS
116 if self.pars.get('RECURSIVE') == 'YES':
117 file_patterns = ["**/%s" % pattern for pattern in file_patterns]
118 nodes = []
119 names = []
120 for node in self.doxy_inputs:
121 if os.path.isdir(node.abspath()):
122 for m in node.ant_glob(incl=file_patterns, excl=exclude_patterns):
123 nodes.append(m)
124 else:
125 nodes.append(node)
126 return (nodes, names)
127
128 def run(self):
129 dct = self.pars.copy()
130 dct['INPUT'] = ' '.join(['"%s"' % x.abspath() for x in self.doxy_inputs])
131 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):
196 '''
197 Check if doxygen and tar commands are present in the system
198
199 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)
Alexander Afanasyev97e4cac2014-03-28 10:55:11 -0700206
207# doxygen docs
208from waflib.Build import BuildContext
209class doxy(BuildContext):
210 cmd = "doxygen"
211 fun = "doxygen"