blob: b9f4e6e2a3d2b8308b438e9ec31cc4824f6c9dbd [file] [log] [blame]
piotr437f5462014-02-04 17:57:25 +01001#
Vasil Velichkov1789ae22019-08-13 20:32:05 +00002# Copyright 2010-2012 Free Software Foundation, Inc.
piotr437f5462014-02-04 17:57:25 +01003#
Vasil Velichkov1789ae22019-08-13 20:32:05 +00004# This file was generated by gr_modtool, a tool from the GNU Radio framework
5# This file is a part of gr-gsm
piotr437f5462014-02-04 17:57:25 +01006#
7# GNU Radio is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 3, or (at your option)
10# any later version.
11#
12# GNU Radio is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with GNU Radio; see the file COPYING. If not, write to
19# the Free Software Foundation, Inc., 51 Franklin Street,
20# Boston, MA 02110-1301, USA.
21#
22"""
23Creates the swig_doc.i SWIG interface file.
24Execute using: python swig_doc.py xml_path outputfilename
25
26The file instructs SWIG to transfer the doxygen comments into the
27python docstrings.
28
29"""
Vasil Velichkov1789ae22019-08-13 20:32:05 +000030from __future__ import unicode_literals
piotr437f5462014-02-04 17:57:25 +010031
Vasil Velichkov1789ae22019-08-13 20:32:05 +000032import sys, time
piotr437f5462014-02-04 17:57:25 +010033
Vasil Velichkov1789ae22019-08-13 20:32:05 +000034from doxyxml import DoxyIndex, DoxyClass, DoxyFriend, DoxyFunction, DoxyFile
35from doxyxml import DoxyOther, base
piotr437f5462014-02-04 17:57:25 +010036
37def py_name(name):
38 bits = name.split('_')
39 return '_'.join(bits[1:])
40
41def make_name(name):
42 bits = name.split('_')
43 return bits[0] + '_make_' + '_'.join(bits[1:])
44
45
46class Block(object):
47 """
48 Checks if doxyxml produced objects correspond to a gnuradio block.
49 """
50
51 @classmethod
52 def includes(cls, item):
53 if not isinstance(item, DoxyClass):
54 return False
55 # Check for a parsing error.
56 if item.error():
57 return False
Vasil Velichkov1789ae22019-08-13 20:32:05 +000058 friendname = make_name(item.name())
59 is_a_block = item.has_member(friendname, DoxyFriend)
60 # But now sometimes the make function isn't a friend so check again.
61 if not is_a_block:
62 is_a_block = di.has_member(friendname, DoxyFunction)
63 return is_a_block
64
65class Block2(object):
66 """
67 Checks if doxyxml produced objects correspond to a new style
68 gnuradio block.
69 """
70
71 @classmethod
72 def includes(cls, item):
73 if not isinstance(item, DoxyClass):
74 return False
75 # Check for a parsing error.
76 if item.error():
77 return False
78 is_a_block2 = item.has_member('make', DoxyFunction) and item.has_member('sptr', DoxyOther)
79 return is_a_block2
piotr437f5462014-02-04 17:57:25 +010080
81
82def utoascii(text):
83 """
Vasil Velichkov1789ae22019-08-13 20:32:05 +000084 Convert unicode text into ascii and escape quotes and backslashes.
piotr437f5462014-02-04 17:57:25 +010085 """
86 if text is None:
87 return ''
88 out = text.encode('ascii', 'replace')
Vasil Velichkov1789ae22019-08-13 20:32:05 +000089 # swig will require us to replace blackslash with 4 backslashes
90 out = out.replace(b'\\', b'\\\\\\\\')
91 out = out.replace(b'"', b'\\"').decode('ascii')
92 return str(out)
piotr437f5462014-02-04 17:57:25 +010093
94
95def combine_descriptions(obj):
96 """
97 Combines the brief and detailed descriptions of an object together.
98 """
99 description = []
100 bd = obj.brief_description.strip()
101 dd = obj.detailed_description.strip()
102 if bd:
103 description.append(bd)
104 if dd:
105 description.append(dd)
106 return utoascii('\n\n'.join(description)).strip()
107
Vasil Velichkov1789ae22019-08-13 20:32:05 +0000108def format_params(parameteritems):
109 output = ['Args:']
110 template = ' {0} : {1}'
111 for pi in parameteritems:
112 output.append(template.format(pi.name, pi.description))
113 return '\n'.join(output)
piotr437f5462014-02-04 17:57:25 +0100114
115entry_templ = '%feature("docstring") {name} "{docstring}"'
Vasil Velichkov1789ae22019-08-13 20:32:05 +0000116def make_entry(obj, name=None, templ="{description}", description=None, params=[]):
piotr437f5462014-02-04 17:57:25 +0100117 """
118 Create a docstring entry for a swig interface file.
119
120 obj - a doxyxml object from which documentation will be extracted.
121 name - the name of the C object (defaults to obj.name())
122 templ - an optional template for the docstring containing only one
123 variable named 'description'.
124 description - if this optional variable is set then it's value is
125 used as the description instead of extracting it from obj.
126 """
127 if name is None:
128 name=obj.name()
129 if "operator " in name:
130 return ''
131 if description is None:
132 description = combine_descriptions(obj)
Vasil Velichkov1789ae22019-08-13 20:32:05 +0000133 if params:
134 description += '\n\n'
135 description += utoascii(format_params(params))
piotr437f5462014-02-04 17:57:25 +0100136 docstring = templ.format(description=description)
137 if not docstring:
138 return ''
139 return entry_templ.format(
140 name=name,
141 docstring=docstring,
142 )
143
144
145def make_func_entry(func, name=None, description=None, params=None):
146 """
147 Create a function docstring entry for a swig interface file.
148
149 func - a doxyxml object from which documentation will be extracted.
150 name - the name of the C object (defaults to func.name())
151 description - if this optional variable is set then it's value is
152 used as the description instead of extracting it from func.
153 params - a parameter list that overrides using func.params.
154 """
Vasil Velichkov1789ae22019-08-13 20:32:05 +0000155 #if params is None:
156 # params = func.params
157 #params = [prm.declname for prm in params]
158 #if params:
159 # sig = "Params: (%s)" % ", ".join(params)
160 #else:
161 # sig = "Params: (NONE)"
162 #templ = "{description}\n\n" + sig
163 #return make_entry(func, name=name, templ=utoascii(templ),
164 # description=description)
165 return make_entry(func, name=name, description=description, params=params)
piotr437f5462014-02-04 17:57:25 +0100166
167
Vasil Velichkov1789ae22019-08-13 20:32:05 +0000168def make_class_entry(klass, description=None, ignored_methods=[], params=None):
piotr437f5462014-02-04 17:57:25 +0100169 """
170 Create a class docstring for a swig interface file.
171 """
Vasil Velichkov1789ae22019-08-13 20:32:05 +0000172 if params is None:
173 params = klass.params
piotr437f5462014-02-04 17:57:25 +0100174 output = []
Vasil Velichkov1789ae22019-08-13 20:32:05 +0000175 output.append(make_entry(klass, description=description, params=params))
piotr437f5462014-02-04 17:57:25 +0100176 for func in klass.in_category(DoxyFunction):
Vasil Velichkov1789ae22019-08-13 20:32:05 +0000177 if func.name() not in ignored_methods:
178 name = klass.name() + '::' + func.name()
179 output.append(make_func_entry(func, name=name))
piotr437f5462014-02-04 17:57:25 +0100180 return "\n\n".join(output)
181
182
183def make_block_entry(di, block):
184 """
185 Create class and function docstrings of a gnuradio block for a
186 swig interface file.
187 """
188 descriptions = []
189 # Get the documentation associated with the class.
190 class_desc = combine_descriptions(block)
191 if class_desc:
192 descriptions.append(class_desc)
193 # Get the documentation associated with the make function
194 make_func = di.get_member(make_name(block.name()), DoxyFunction)
195 make_func_desc = combine_descriptions(make_func)
196 if make_func_desc:
197 descriptions.append(make_func_desc)
198 # Get the documentation associated with the file
199 try:
200 block_file = di.get_member(block.name() + ".h", DoxyFile)
201 file_desc = combine_descriptions(block_file)
202 if file_desc:
203 descriptions.append(file_desc)
204 except base.Base.NoSuchMember:
205 # Don't worry if we can't find a matching file.
206 pass
207 # And join them all together to make a super duper description.
208 super_description = "\n\n".join(descriptions)
209 # Associate the combined description with the class and
210 # the make function.
211 output = []
212 output.append(make_class_entry(block, description=super_description))
piotr437f5462014-02-04 17:57:25 +0100213 output.append(make_func_entry(make_func, description=super_description,
Vasil Velichkov1789ae22019-08-13 20:32:05 +0000214 params=block.params))
piotr437f5462014-02-04 17:57:25 +0100215 return "\n\n".join(output)
216
Vasil Velichkov1789ae22019-08-13 20:32:05 +0000217def make_block2_entry(di, block):
218 """
219 Create class and function docstrings of a new style gnuradio block for a
220 swig interface file.
221 """
222 descriptions = []
223 # For new style blocks all the relevant documentation should be
224 # associated with the 'make' method.
225 class_description = combine_descriptions(block)
226 make_func = block.get_member('make', DoxyFunction)
227 make_description = combine_descriptions(make_func)
228 description = class_description + "\n\nConstructor Specific Documentation:\n\n" + make_description
229 # Associate the combined description with the class and
230 # the make function.
231 output = []
232 output.append(make_class_entry(
233 block, description=description,
234 ignored_methods=['make'], params=make_func.params))
235 makename = block.name() + '::make'
236 output.append(make_func_entry(
237 make_func, name=makename, description=description,
238 params=make_func.params))
239 return "\n\n".join(output)
piotr437f5462014-02-04 17:57:25 +0100240
241def make_swig_interface_file(di, swigdocfilename, custom_output=None):
242
243 output = ["""
244/*
245 * This file was automatically generated using swig_doc.py.
246 *
247 * Any changes to it will be lost next time it is regenerated.
248 */
249"""]
250
251 if custom_output is not None:
252 output.append(custom_output)
253
254 # Create docstrings for the blocks.
255 blocks = di.in_category(Block)
Vasil Velichkov1789ae22019-08-13 20:32:05 +0000256 blocks2 = di.in_category(Block2)
257
piotr437f5462014-02-04 17:57:25 +0100258 make_funcs = set([])
259 for block in blocks:
260 try:
261 make_func = di.get_member(make_name(block.name()), DoxyFunction)
Vasil Velichkov1789ae22019-08-13 20:32:05 +0000262 # Don't want to risk writing to output twice.
263 if make_func.name() not in make_funcs:
264 make_funcs.add(make_func.name())
265 output.append(make_block_entry(di, block))
piotr437f5462014-02-04 17:57:25 +0100266 except block.ParsingError:
Vasil Velichkov1789ae22019-08-13 20:32:05 +0000267 sys.stderr.write('Parsing error for block {0}\n'.format(block.name()))
268 raise
269
270 for block in blocks2:
271 try:
272 make_func = block.get_member('make', DoxyFunction)
273 make_func_name = block.name() +'::make'
274 # Don't want to risk writing to output twice.
275 if make_func_name not in make_funcs:
276 make_funcs.add(make_func_name)
277 output.append(make_block2_entry(di, block))
278 except block.ParsingError:
279 sys.stderr.write('Parsing error for block {0}\n'.format(block.name()))
280 raise
piotr437f5462014-02-04 17:57:25 +0100281
282 # Create docstrings for functions
283 # Don't include the make functions since they have already been dealt with.
Vasil Velichkov1789ae22019-08-13 20:32:05 +0000284 funcs = [f for f in di.in_category(DoxyFunction)
285 if f.name() not in make_funcs and not f.name().startswith('std::')]
piotr437f5462014-02-04 17:57:25 +0100286 for f in funcs:
287 try:
288 output.append(make_func_entry(f))
289 except f.ParsingError:
Vasil Velichkov1789ae22019-08-13 20:32:05 +0000290 sys.stderr.write('Parsing error for function {0}\n'.format(f.name()))
piotr437f5462014-02-04 17:57:25 +0100291
292 # Create docstrings for classes
293 block_names = [block.name() for block in blocks]
Vasil Velichkov1789ae22019-08-13 20:32:05 +0000294 block_names += [block.name() for block in blocks2]
295 klasses = [k for k in di.in_category(DoxyClass)
296 if k.name() not in block_names and not k.name().startswith('std::')]
piotr437f5462014-02-04 17:57:25 +0100297 for k in klasses:
298 try:
299 output.append(make_class_entry(k))
300 except k.ParsingError:
Vasil Velichkov1789ae22019-08-13 20:32:05 +0000301 sys.stderr.write('Parsing error for class {0}\n'.format(k.name()))
piotr437f5462014-02-04 17:57:25 +0100302
303 # Docstrings are not created for anything that is not a function or a class.
304 # If this excludes anything important please add it here.
305
306 output = "\n\n".join(output)
307
Vasil Velichkov1789ae22019-08-13 20:32:05 +0000308 swig_doc = open(swigdocfilename, 'w')
piotr437f5462014-02-04 17:57:25 +0100309 swig_doc.write(output)
310 swig_doc.close()
311
312if __name__ == "__main__":
313 # Parse command line options and set up doxyxml.
314 err_msg = "Execute using: python swig_doc.py xml_path outputfilename"
315 if len(sys.argv) != 3:
Vasil Velichkov1789ae22019-08-13 20:32:05 +0000316 raise Exception(err_msg)
piotr437f5462014-02-04 17:57:25 +0100317 xml_path = sys.argv[1]
318 swigdocfilename = sys.argv[2]
319 di = DoxyIndex(xml_path)
320
321 # gnuradio.gr.msq_queue.insert_tail and delete_head create errors unless docstrings are defined!
322 # This is presumably a bug in SWIG.
323 #msg_q = di.get_member(u'gr_msg_queue', DoxyClass)
324 #insert_tail = msg_q.get_member(u'insert_tail', DoxyFunction)
325 #delete_head = msg_q.get_member(u'delete_head', DoxyFunction)
326 output = []
327 #output.append(make_func_entry(insert_tail, name='gr_py_msg_queue__insert_tail'))
328 #output.append(make_func_entry(delete_head, name='gr_py_msg_queue__delete_head'))
329 custom_output = "\n\n".join(output)
330
331 # Generate the docstrings interface file.
332 make_swig_interface_file(di, swigdocfilename, custom_output=custom_output)