blob: 694819458bbd1b328da1cc511f58b636d69c13c9 [file] [log] [blame]
Alexander Afanasyeva1ae0a12014-01-28 15:21:02 -08001# Thomas Nagy 2008-2010 (ita)
2
3"""
Alexander Afanasyeva1ae0a12014-01-28 15:21:02 -08004Doxygen support
5
6Variables passed to bld():
7* doxyfile -- the Doxyfile to use
8
Alexander Afanasyev5e1288e2014-03-28 11:11:48 -07009When 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', ...)
21
22 def doxygen(bld):
23 if bld.env.DOXYGEN:
24 bld(features="doxygen", doxyfile='Doxyfile', ...)
Alexander Afanasyeva1ae0a12014-01-28 15:21:02 -080025"""
26
Davide Pesaventoac537892024-03-11 20:30:08 -040027import os, os.path, re
28from waflib import Task, Utils, Node
Alexander Afanasyeva1ae0a12014-01-28 15:21:02 -080029from waflib.TaskGen import feature
30
Alexander Afanasyev5e1288e2014-03-28 11:11:48 -070031DOXY_STR = '"${DOXYGEN}" - '
Alexander Afanasyeva1ae0a12014-01-28 15:21:02 -080032DOXY_FMTS = 'html latex man rft xml'.split()
33DOXY_FILE_PATTERNS = '*.' + ' *.'.join('''
34c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx hpp h++ idl odl cs php php3
35inc m mm py f90c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx
36'''.split())
37
38re_rl = re.compile('\\\\\r*\n', re.MULTILINE)
39re_nl = re.compile('\r*\n', re.M)
40def parse_doxy(txt):
41 tbl = {}
42 txt = re_rl.sub('', txt)
43 lines = re_nl.split(txt)
44 for x in lines:
45 x = x.strip()
46 if not x or x.startswith('#') or x.find('=') < 0:
47 continue
48 if x.find('+=') >= 0:
49 tmp = x.split('+=')
50 key = tmp[0].strip()
51 if key in tbl:
52 tbl[key] += ' ' + '+='.join(tmp[1:]).strip()
53 else:
54 tbl[key] = '+='.join(tmp[1:]).strip()
55 else:
56 tmp = x.split('=')
57 tbl[tmp[0].strip()] = '='.join(tmp[1:]).strip()
58 return tbl
59
60class doxygen(Task.Task):
61 vars = ['DOXYGEN', 'DOXYFLAGS']
62 color = 'BLUE'
63
64 def runnable_status(self):
65 '''
66 self.pars are populated in runnable_status - because this function is being
67 run *before* both self.pars "consumers" - scan() and run()
68
69 set output_dir (node) for the output
70 '''
71
72 for x in self.run_after:
73 if not x.hasrun:
74 return Task.ASK_LATER
75
76 if not getattr(self, 'pars', None):
77 txt = self.inputs[0].read()
78 self.pars = parse_doxy(txt)
79 if not self.pars.get('OUTPUT_DIRECTORY'):
80 self.pars['OUTPUT_DIRECTORY'] = self.inputs[0].parent.get_bld().abspath()
81
Alexander Afanasyev5e1288e2014-03-28 11:11:48 -070082 # Override with any parameters passed to the task generator
83 if getattr(self.generator, 'pars', None):
Alexander Afanasyev8c6880f2016-03-02 15:38:20 -080084 for k, v in self.generator.pars.items():
Alexander Afanasyev5e1288e2014-03-28 11:11:48 -070085 self.pars[k] = v
86
Alexander Afanasyeva1ae0a12014-01-28 15:21:02 -080087 self.doxy_inputs = getattr(self, 'doxy_inputs', [])
88 if not self.pars.get('INPUT'):
89 self.doxy_inputs.append(self.inputs[0].parent)
90 else:
91 for i in self.pars.get('INPUT').split():
92 if os.path.isabs(i):
93 node = self.generator.bld.root.find_node(i)
94 else:
95 node = self.generator.path.find_node(i)
96 if not node:
97 self.generator.bld.fatal('Could not find the doxygen input %r' % i)
98 self.doxy_inputs.append(node)
99
100 if not getattr(self, 'output_dir', None):
101 bld = self.generator.bld
102 # First try to find an absolute path, then find or declare a relative path
103 self.output_dir = bld.root.find_dir(self.pars['OUTPUT_DIRECTORY'])
104 if not self.output_dir:
105 self.output_dir = bld.path.find_or_declare(self.pars['OUTPUT_DIRECTORY'])
106
107 self.signature()
108 return Task.Task.runnable_status(self)
109
110 def scan(self):
Alexander Afanasyev5e1288e2014-03-28 11:11:48 -0700111 exclude_patterns = self.pars.get('EXCLUDE_PATTERNS','').split()
112 file_patterns = self.pars.get('FILE_PATTERNS','').split()
Alexander Afanasyeva1ae0a12014-01-28 15:21:02 -0800113 if not file_patterns:
114 file_patterns = DOXY_FILE_PATTERNS
Alexander Afanasyev5e1288e2014-03-28 11:11:48 -0700115 if self.pars.get('RECURSIVE') == 'YES':
116 file_patterns = ["**/%s" % pattern for pattern in file_patterns]
Alexander Afanasyeva1ae0a12014-01-28 15:21:02 -0800117 nodes = []
118 names = []
119 for node in self.doxy_inputs:
120 if os.path.isdir(node.abspath()):
Alexander Afanasyev5e1288e2014-03-28 11:11:48 -0700121 for m in node.ant_glob(incl=file_patterns, excl=exclude_patterns):
Alexander Afanasyeva1ae0a12014-01-28 15:21:02 -0800122 nodes.append(m)
123 else:
124 nodes.append(node)
125 return (nodes, names)
126
127 def run(self):
128 dct = self.pars.copy()
Alexander Afanasyev5e1288e2014-03-28 11:11:48 -0700129 dct['INPUT'] = ' '.join(['"%s"' % x.abspath() for x in self.doxy_inputs])
Alexander Afanasyeva1ae0a12014-01-28 15:21:02 -0800130 code = '\n'.join(['%s = %s' % (x, dct[x]) for x in self.pars])
131 code = code.encode() # for python 3
132 #fmt = DOXY_STR % (self.inputs[0].parent.abspath())
133 cmd = Utils.subst_vars(DOXY_STR, self.env)
134 env = self.env.env or None
135 proc = Utils.subprocess.Popen(cmd, shell=True, stdin=Utils.subprocess.PIPE, env=env, cwd=self.generator.bld.path.get_bld().abspath())
136 proc.communicate(code)
137 return proc.returncode
138
139 def post_run(self):
140 nodes = self.output_dir.ant_glob('**/*', quiet=True)
141 for x in nodes:
142 x.sig = Utils.h_file(x.abspath())
143 self.outputs += nodes
144 return Task.Task.post_run(self)
145
146class tar(Task.Task):
147 "quick tar creation"
148 run_str = '${TAR} ${TAROPTS} ${TGT} ${SRC}'
149 color = 'RED'
150 after = ['doxygen']
151 def runnable_status(self):
152 for x in getattr(self, 'input_tasks', []):
153 if not x.hasrun:
154 return Task.ASK_LATER
155
156 if not getattr(self, 'tar_done_adding', None):
157 # execute this only once
158 self.tar_done_adding = True
159 for x in getattr(self, 'input_tasks', []):
160 self.set_inputs(x.outputs)
161 if not self.inputs:
162 return Task.SKIP_ME
163 return Task.Task.runnable_status(self)
164
165 def __str__(self):
166 tgt_str = ' '.join([a.nice_path(self.env) for a in self.outputs])
167 return '%s: %s\n' % (self.__class__.__name__, tgt_str)
168
169@feature('doxygen')
170def process_doxy(self):
171 if not getattr(self, 'doxyfile', None):
172 self.generator.bld.fatal('no doxyfile??')
173
174 node = self.doxyfile
175 if not isinstance(node, Node.Node):
176 node = self.path.find_resource(node)
177 if not node:
178 raise ValueError('doxygen file not found')
179
180 # the task instance
181 dsk = self.create_task('doxygen', node)
182
183 if getattr(self, 'doxy_tar', None):
184 tsk = self.create_task('tar')
185 tsk.input_tasks = [dsk]
186 tsk.set_outputs(self.path.find_or_declare(self.doxy_tar))
187 if self.doxy_tar.endswith('bz2'):
188 tsk.env['TAROPTS'] = ['cjf']
189 elif self.doxy_tar.endswith('gz'):
190 tsk.env['TAROPTS'] = ['czf']
191 else:
192 tsk.env['TAROPTS'] = ['cf']
193
194def configure(conf):
Alexander Afanasyev5e1288e2014-03-28 11:11:48 -0700195 '''
196 Check if doxygen and tar commands are present in the system
Alexander Afanasyeva1ae0a12014-01-28 15:21:02 -0800197
Alexander Afanasyev5e1288e2014-03-28 11:11:48 -0700198 If the commands are present, then conf.env.DOXYGEN and conf.env.TAR
199 variables will be set. Detection can be controlled by setting DOXYGEN and
200 TAR environmental variables.
201 '''
202
203 conf.find_program('doxygen', var='DOXYGEN', mandatory=False)
204 conf.find_program('tar', var='TAR', mandatory=False)
205
Davide Pesaventoac537892024-03-11 20:30:08 -0400206# doxygen command
Alexander Afanasyev5e1288e2014-03-28 11:11:48 -0700207from waflib.Build import BuildContext
208class doxy(BuildContext):
Davide Pesaventoac537892024-03-11 20:30:08 -0400209 cmd = 'doxygen'
210 fun = 'doxygen'