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 file_patterns
= self
.pars
.get('FILE_PATTERNS','').split()
123 if not file_patterns
:
124 file_patterns
= DOXY_FILE_PATTERNS
125 if self
.pars
.get('RECURSIVE') == 'YES':
126 file_patterns
= ["**/%s" % pattern
for pattern
in file_patterns
]
129 for node
in self
.doxy_inputs
:
130 if os
.path
.isdir(node
.abspath()):
131 for m
in node
.ant_glob(incl
=file_patterns
, excl
=exclude_patterns
):
135 return (nodes
, names
)
138 dct
= self
.pars
.copy()
139 code
= '\n'.join(['%s = %s' % (x
, dct
[x
]) for x
in self
.pars
])
140 code
= code
.encode() # for python 3
141 #fmt = DOXY_STR % (self.inputs[0].parent.abspath())
142 cmd
= Utils
.subst_vars(DOXY_STR
, self
.env
)
143 env
= self
.env
.env
or None
144 proc
= Utils
.subprocess
.Popen(cmd
, shell
=True, stdin
=Utils
.subprocess
.PIPE
, env
=env
, cwd
=self
.inputs
[0].parent
.abspath())
145 proc
.communicate(code
)
146 return proc
.returncode
149 nodes
= self
.output_dir
.ant_glob('**/*', quiet
=True)
151 x
.sig
= Utils
.h_file(x
.abspath())
153 return Task
.Task
.post_run(self
)
155 def add_install(self
):
156 nodes
= self
.output_dir
.ant_glob('**/*', quiet
=True)
157 self
.outputs
+= nodes
158 if getattr(self
.generator
, 'install_path', None):
159 if not getattr(self
.generator
, 'doxy_tar', None):
160 self
.generator
.bld
.install_files(self
.generator
.install_path
,
166 class tar(Task
.Task
):
168 run_str
= '${TAR} ${TAROPTS} ${TGT} ${SRC}'
171 def runnable_status(self
):
172 for x
in getattr(self
, 'input_tasks', []):
174 return Task
.ASK_LATER
176 if not getattr(self
, 'tar_done_adding', None):
177 # execute this only once
178 self
.tar_done_adding
= True
179 for x
in getattr(self
, 'input_tasks', []):
180 self
.set_inputs(x
.outputs
)
183 return Task
.Task
.runnable_status(self
)
186 tgt_str
= ' '.join([a
.path_from(a
.ctx
.launch_node()) for a
in self
.outputs
])
187 return '%s: %s\n' % (self
.__class
__.__name
__, tgt_str
)
190 def process_doxy(self
):
191 if not getattr(self
, 'doxyfile', None):
192 self
.generator
.bld
.fatal('no doxyfile??')
195 if not isinstance(node
, Node
.Node
):
196 node
= self
.path
.find_resource(node
)
198 raise ValueError('doxygen file not found')
201 dsk
= self
.create_task('doxygen', node
)
203 if getattr(self
, 'doxy_tar', None):
204 tsk
= self
.create_task('tar')
205 tsk
.input_tasks
= [dsk
]
206 tsk
.set_outputs(self
.path
.find_or_declare(self
.doxy_tar
))
207 if self
.doxy_tar
.endswith('bz2'):
208 tsk
.env
['TAROPTS'] = ['cjf']
209 elif self
.doxy_tar
.endswith('gz'):
210 tsk
.env
['TAROPTS'] = ['czf']
212 tsk
.env
['TAROPTS'] = ['cf']
213 if getattr(self
, 'install_path', None):
214 self
.bld
.install_files(self
.install_path
, tsk
.outputs
)
218 Check if doxygen and tar commands are present in the system
220 If the commands are present, then conf.env.DOXYGEN and conf.env.TAR
221 variables will be set. Detection can be controlled by setting DOXYGEN and
222 TAR environmental variables.
225 conf
.find_program('doxygen', var
='DOXYGEN', mandatory
=False)
226 conf
.find_program('tar', var
='TAR', mandatory
=False)