thirdparty:waf: New files for waf 1.9.10
[Samba.git] / third_party / waf / waflib / extras / doxygen.py
blob9e17595f89a050f37840561dc8df8fe478351913
1 #! /usr/bin/env python
2 # encoding: UTF-8
3 # Thomas Nagy 2008-2010 (ita)
5 """
7 Doxygen support
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:
17 def options(opt):
18 opt.load('doxygen')
20 def configure(conf):
21 conf.load('doxygen')
22 # check conf.env.DOXYGEN, if it is mandatory
24 def build(bld):
25 if bld.env.DOXYGEN:
26 bld(features="doxygen", doxyfile='Doxyfile', ...)
27 """
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
38 '''.split())
40 re_rl = re.compile('\\\\\r*\n', re.MULTILINE)
41 re_nl = re.compile('\r*\n', re.M)
42 def parse_doxy(txt):
43 tbl = {}
44 txt = re_rl.sub('', txt)
45 lines = re_nl.split(txt)
46 for x in lines:
47 x = x.strip()
48 if not x or x.startswith('#') or x.find('=') < 0:
49 continue
50 if x.find('+=') >= 0:
51 tmp = x.split('+=')
52 key = tmp[0].strip()
53 if key in tbl:
54 tbl[key] += ' ' + '+='.join(tmp[1:]).strip()
55 else:
56 tbl[key] = '+='.join(tmp[1:]).strip()
57 else:
58 tmp = x.split('=')
59 tbl[tmp[0].strip()] = '='.join(tmp[1:]).strip()
60 return tbl
62 class doxygen(Task.Task):
63 vars = ['DOXYGEN', 'DOXYFLAGS']
64 color = 'BLUE'
66 def runnable_status(self):
67 '''
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
72 '''
74 for x in self.run_after:
75 if not x.hasrun:
76 return Task.ASK_LATER
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'])
84 else:
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')
87 output_node.mkdir()
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():
93 self.pars[k] = v
95 self.doxy_inputs = getattr(self, 'doxy_inputs', [])
96 if not self.pars.get('INPUT'):
97 self.doxy_inputs.append(self.inputs[0].parent)
98 else:
99 for i in self.pars.get('INPUT').split():
100 if os.path.isabs(i):
101 node = self.generator.bld.root.find_node(i)
102 else:
103 node = self.inputs[0].parent.find_node(i)
104 if not node:
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'])
113 self.signature()
114 ret = Task.Task.runnable_status(self)
115 if ret == Task.SKIP_ME:
116 # in case the files were removed
117 self.add_install()
118 return ret
120 def scan(self):
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]
127 nodes = []
128 names = []
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):
132 nodes.append(m)
133 else:
134 nodes.append(node)
135 return (nodes, names)
137 def run(self):
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
148 def post_run(self):
149 nodes = self.output_dir.ant_glob('**/*', quiet=True)
150 for x in nodes:
151 x.sig = Utils.h_file(x.abspath())
152 self.add_install()
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,
161 self.outputs,
162 postpone=False,
163 cwd=self.output_dir,
164 relative_trick=True)
166 class tar(Task.Task):
167 "quick tar creation"
168 run_str = '${TAR} ${TAROPTS} ${TGT} ${SRC}'
169 color = 'RED'
170 after = ['doxygen']
171 def runnable_status(self):
172 for x in getattr(self, 'input_tasks', []):
173 if not x.hasrun:
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)
181 if not self.inputs:
182 return Task.SKIP_ME
183 return Task.Task.runnable_status(self)
185 def __str__(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)
189 @feature('doxygen')
190 def process_doxy(self):
191 if not getattr(self, 'doxyfile', None):
192 self.generator.bld.fatal('no doxyfile??')
194 node = self.doxyfile
195 if not isinstance(node, Node.Node):
196 node = self.path.find_resource(node)
197 if not node:
198 raise ValueError('doxygen file not found')
200 # the task instance
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']
211 else:
212 tsk.env['TAROPTS'] = ['cf']
213 if getattr(self, 'install_path', None):
214 self.bld.install_files(self.install_path, tsk.outputs)
216 def configure(conf):
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)