3 # Thomas Nagy 2008-2010 (ita)
9 Variables passed to bld():
10 * doxyfile -- the Doxyfile to use
11 * doxy_tar -- destination archive for generated documentation (if desired)
12 * install_path -- where to install the documentation
13 * pars -- dictionary overriding doxygen configuration settings
15 When using this tool, the wscript will look like:
22 # check conf.env.DOXYGEN, if it is mandatory
26 bld(features="doxygen", doxyfile='Doxyfile', ...)
29 import os, os.path, re
30 from collections import OrderedDict
31 from waflib import Task, Utils, Node
32 from waflib.TaskGen import feature
34 DOXY_STR = '"${DOXYGEN}" - '
35 DOXY_FMTS = 'html latex man rft xml'.split()
36 DOXY_FILE_PATTERNS = '*.' + ' *.'.join('''
37 c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx hpp h++ idl odl cs php php3
38 inc m mm py f90c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx
41 re_rl = re.compile('\\\\\r*\n', re.MULTILINE)
42 re_nl = re.compile('\r*\n', re.M)
45 Parses a doxygen file.
46 Returns an ordered dictionary. We cannot return a default dictionary, as the
47 order in which the entries are reported does matter, especially for the
51 txt = re_rl.sub('', txt)
52 lines = re_nl.split(txt)
55 if not x or x.startswith('#') or x.find('=') < 0:
61 tbl[key] += ' ' + '+='.join(tmp[1:]).strip()
63 tbl[key] = '+='.join(tmp[1:]).strip()
66 tbl[tmp[0].strip()] = '='.join(tmp[1:]).strip()
69 class doxygen(Task.Task):
70 vars = ['DOXYGEN', 'DOXYFLAGS']
73 def runnable_status(self):
75 self.pars are populated in runnable_status - because this function is being
76 run *before* both self.pars "consumers" - scan() and run()
78 set output_dir (node) for the output
81 for x in self.run_after:
85 if not getattr(self, 'pars', None):
86 txt = self.inputs[0].read()
87 self.pars = parse_doxy(txt)
88 if self.pars.get('OUTPUT_DIRECTORY'):
89 # Use the path parsed from the Doxyfile as an absolute path
90 output_node = self.inputs[0].parent.get_bld().make_node(self.pars['OUTPUT_DIRECTORY'])
92 # If no OUTPUT_PATH was specified in the Doxyfile, build path from the Doxyfile name + '.doxy'
93 output_node = self.inputs[0].parent.get_bld().make_node(self.inputs[0].name + '.doxy')
95 self.pars['OUTPUT_DIRECTORY'] = output_node.abspath()
97 # Override with any parameters passed to the task generator
98 if getattr(self.generator, 'pars', None):
99 for k, v in self.generator.pars.items():
102 self.doxy_inputs = getattr(self, 'doxy_inputs', [])
103 if not self.pars.get('INPUT'):
104 self.doxy_inputs.append(self.inputs[0].parent)
106 for i in self.pars.get('INPUT').split():
108 node = self.generator.bld.root.find_node(i)
110 node = self.inputs[0].parent.find_node(i)
112 self.generator.bld.fatal('Could not find the doxygen input %r' % i)
113 self.doxy_inputs.append(node)
115 if not getattr(self, 'output_dir', None):
116 bld = self.generator.bld
117 # Output path is always an absolute path as it was transformed above.
118 self.output_dir = bld.root.find_dir(self.pars['OUTPUT_DIRECTORY'])
121 ret = Task.Task.runnable_status(self)
122 if ret == Task.SKIP_ME:
123 # in case the files were removed
128 exclude_patterns = self.pars.get('EXCLUDE_PATTERNS','').split()
129 exclude_patterns = [pattern.replace('*/', '**/') for pattern in exclude_patterns]
130 file_patterns = self.pars.get('FILE_PATTERNS','').split()
131 if not file_patterns:
132 file_patterns = DOXY_FILE_PATTERNS.split()
133 if self.pars.get('RECURSIVE') == 'YES':
134 file_patterns = ["**/%s" % pattern for pattern in file_patterns]
137 for node in self.doxy_inputs:
138 if os.path.isdir(node.abspath()):
139 for m in node.ant_glob(incl=file_patterns, excl=exclude_patterns):
143 return (nodes, names)
146 dct = self.pars.copy()
147 code = '\n'.join(['%s = %s' % (x, dct[x]) for x in self.pars])
148 code = code.encode() # for python 3
149 #fmt = DOXY_STR % (self.inputs[0].parent.abspath())
150 cmd = Utils.subst_vars(DOXY_STR, self.env)
151 env = self.env.env or None
152 proc = Utils.subprocess.Popen(cmd, shell=True, stdin=Utils.subprocess.PIPE, env=env, cwd=self.inputs[0].parent.abspath())
153 proc.communicate(code)
154 return proc.returncode
157 nodes = self.output_dir.ant_glob('**/*', quiet=True)
159 self.generator.bld.node_sigs[x] = self.uid()
161 return Task.Task.post_run(self)
163 def add_install(self):
164 nodes = self.output_dir.ant_glob('**/*', quiet=True)
165 self.outputs += nodes
166 if getattr(self.generator, 'install_path', None):
167 if not getattr(self.generator, 'doxy_tar', None):
168 self.generator.add_install_files(install_to=self.generator.install_path,
169 install_from=self.outputs,
174 class tar(Task.Task):
176 run_str = '${TAR} ${TAROPTS} ${TGT} ${SRC}'
179 def runnable_status(self):
180 for x in getattr(self, 'input_tasks', []):
182 return Task.ASK_LATER
184 if not getattr(self, 'tar_done_adding', None):
185 # execute this only once
186 self.tar_done_adding = True
187 for x in getattr(self, 'input_tasks', []):
188 self.set_inputs(x.outputs)
191 return Task.Task.runnable_status(self)
194 tgt_str = ' '.join([a.path_from(a.ctx.launch_node()) for a in self.outputs])
195 return '%s: %s\n' % (self.__class__.__name__, tgt_str)
198 def process_doxy(self):
199 if not getattr(self, 'doxyfile', None):
200 self.bld.fatal('no doxyfile variable specified??')
203 if not isinstance(node, Node.Node):
204 node = self.path.find_resource(node)
206 self.bld.fatal('doxygen file %s not found' % self.doxyfile)
209 dsk = self.create_task('doxygen', node)
211 if getattr(self, 'doxy_tar', None):
212 tsk = self.create_task('tar')
213 tsk.input_tasks = [dsk]
214 tsk.set_outputs(self.path.find_or_declare(self.doxy_tar))
215 if self.doxy_tar.endswith('bz2'):
216 tsk.env['TAROPTS'] = ['cjf']
217 elif self.doxy_tar.endswith('gz'):
218 tsk.env['TAROPTS'] = ['czf']
220 tsk.env['TAROPTS'] = ['cf']
221 if getattr(self, 'install_path', None):
222 self.add_install_files(install_to=self.install_path, install_from=tsk.outputs)
226 Check if doxygen and tar commands are present in the system
228 If the commands are present, then conf.env.DOXYGEN and conf.env.TAR
229 variables will be set. Detection can be controlled by setting DOXYGEN and
230 TAR environmental variables.
233 conf.find_program('doxygen', var='DOXYGEN', mandatory=False)
234 conf.find_program('tar', var='TAR', mandatory=False)