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 waflib import Task, Utils, Node
31 from waflib.TaskGen import feature
33 DOXY_STR = '"${DOXYGEN}" - '
34 DOXY_FMTS = 'html latex man rft xml'.split()
35 DOXY_FILE_PATTERNS = '*.' + ' *.'.join('''
36 c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx hpp h++ idl odl cs php php3
37 inc m mm py f90c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx
40 re_rl = re.compile('\\\\\r*\n', re.MULTILINE)
41 re_nl = re.compile('\r*\n', re.M)
44 txt = re_rl.sub('', txt)
45 lines = re_nl.split(txt)
48 if not x or x.startswith('#') or x.find('=') < 0:
54 tbl[key] += ' ' + '+='.join(tmp[1:]).strip()
56 tbl[key] = '+='.join(tmp[1:]).strip()
59 tbl[tmp[0].strip()] = '='.join(tmp[1:]).strip()
62 class doxygen(Task.Task):
63 vars = ['DOXYGEN', 'DOXYFLAGS']
66 def runnable_status(self):
68 self.pars are populated in runnable_status - because this function is being
69 run *before* both self.pars "consumers" - scan() and run()
71 set output_dir (node) for the output
74 for x in self.run_after:
78 if not getattr(self, 'pars', None):
79 txt = self.inputs[0].read()
80 self.pars = parse_doxy(txt)
81 if self.pars.get('OUTPUT_DIRECTORY'):
82 # Use the path parsed from the Doxyfile as an absolute path
83 output_node = self.inputs[0].parent.get_bld().make_node(self.pars['OUTPUT_DIRECTORY'])
85 # If no OUTPUT_PATH was specified in the Doxyfile, build path from the Doxyfile name + '.doxy'
86 output_node = self.inputs[0].parent.get_bld().make_node(self.inputs[0].name + '.doxy')
88 self.pars['OUTPUT_DIRECTORY'] = output_node.abspath()
90 # Override with any parameters passed to the task generator
91 if getattr(self.generator, 'pars', None):
92 for k, v in self.generator.pars.items():
95 self.doxy_inputs = getattr(self, 'doxy_inputs', [])
96 if not self.pars.get('INPUT'):
97 self.doxy_inputs.append(self.inputs[0].parent)
99 for i in self.pars.get('INPUT').split():
101 node = self.generator.bld.root.find_node(i)
103 node = self.inputs[0].parent.find_node(i)
105 self.generator.bld.fatal('Could not find the doxygen input %r' % i)
106 self.doxy_inputs.append(node)
108 if not getattr(self, 'output_dir', None):
109 bld = self.generator.bld
110 # Output path is always an absolute path as it was transformed above.
111 self.output_dir = bld.root.find_dir(self.pars['OUTPUT_DIRECTORY'])
114 ret = Task.Task.runnable_status(self)
115 if ret == Task.SKIP_ME:
116 # in case the files were removed
121 exclude_patterns = self.pars.get('EXCLUDE_PATTERNS','').split()
122 exclude_patterns = [pattern.replace('*/', '**/') for pattern in exclude_patterns]
123 file_patterns = self.pars.get('FILE_PATTERNS','').split()
124 if not file_patterns:
125 file_patterns = DOXY_FILE_PATTERNS.split()
126 if self.pars.get('RECURSIVE') == 'YES':
127 file_patterns = ["**/%s" % pattern for pattern in file_patterns]
130 for node in self.doxy_inputs:
131 if os.path.isdir(node.abspath()):
132 for m in node.ant_glob(incl=file_patterns, excl=exclude_patterns):
136 return (nodes, names)
139 dct = self.pars.copy()
140 code = '\n'.join(['%s = %s' % (x, dct[x]) for x in self.pars])
141 code = code.encode() # for python 3
142 #fmt = DOXY_STR % (self.inputs[0].parent.abspath())
143 cmd = Utils.subst_vars(DOXY_STR, self.env)
144 env = self.env.env or None
145 proc = Utils.subprocess.Popen(cmd, shell=True, stdin=Utils.subprocess.PIPE, env=env, cwd=self.inputs[0].parent.abspath())
146 proc.communicate(code)
147 return proc.returncode
150 nodes = self.output_dir.ant_glob('**/*', quiet=True)
152 self.generator.bld.node_sigs[x] = self.uid()
154 return Task.Task.post_run(self)
156 def add_install(self):
157 nodes = self.output_dir.ant_glob('**/*', quiet=True)
158 self.outputs += nodes
159 if getattr(self.generator, 'install_path', None):
160 if not getattr(self.generator, 'doxy_tar', None):
161 self.generator.add_install_files(install_to=self.generator.install_path,
162 install_from=self.outputs,
167 class tar(Task.Task):
169 run_str = '${TAR} ${TAROPTS} ${TGT} ${SRC}'
172 def runnable_status(self):
173 for x in getattr(self, 'input_tasks', []):
175 return Task.ASK_LATER
177 if not getattr(self, 'tar_done_adding', None):
178 # execute this only once
179 self.tar_done_adding = True
180 for x in getattr(self, 'input_tasks', []):
181 self.set_inputs(x.outputs)
184 return Task.Task.runnable_status(self)
187 tgt_str = ' '.join([a.path_from(a.ctx.launch_node()) for a in self.outputs])
188 return '%s: %s\n' % (self.__class__.__name__, tgt_str)
191 def process_doxy(self):
192 if not getattr(self, 'doxyfile', None):
193 self.bld.fatal('no doxyfile variable specified??')
196 if not isinstance(node, Node.Node):
197 node = self.path.find_resource(node)
199 self.bld.fatal('doxygen file %s not found' % self.doxyfile)
202 dsk = self.create_task('doxygen', node)
204 if getattr(self, 'doxy_tar', None):
205 tsk = self.create_task('tar')
206 tsk.input_tasks = [dsk]
207 tsk.set_outputs(self.path.find_or_declare(self.doxy_tar))
208 if self.doxy_tar.endswith('bz2'):
209 tsk.env['TAROPTS'] = ['cjf']
210 elif self.doxy_tar.endswith('gz'):
211 tsk.env['TAROPTS'] = ['czf']
213 tsk.env['TAROPTS'] = ['cf']
214 if getattr(self, 'install_path', None):
215 self.add_install_files(install_to=self.install_path, install_from=tsk.outputs)
219 Check if doxygen and tar commands are present in the system
221 If the commands are present, then conf.env.DOXYGEN and conf.env.TAR
222 variables will be set. Detection can be controlled by setting DOXYGEN and
223 TAR environmental variables.
226 conf.find_program('doxygen', var='DOXYGEN', mandatory=False)
227 conf.find_program('tar', var='TAR', mandatory=False)